# Student Funnel & Flows

This document details the student-facing marketing funnel, deep-link formats, and the quiz flow runtime.

---

## 1. Funnel entry via Deep Links

Students enter the funnel by tapping a Telegram deep link with a payload:

| Deep Link Format | Parsed Payload (`StartPayloadParser`) | Behavior |
|---|---|---|
| `t.me/bot?start=t_{slug}` | `test_slug = {slug}` | Directly starts the specified active test. **No intro text or description is shown.** (Product Rule §1). |
| `t.me/bot?start=t_{slug}__c_{campaign_code}` | `test_slug = {slug}`, `campaign_code = {campaign_code}` | Starts the test, plus attributes the user's first-touch to the specified campaign. |
| `t.me/bot?start=c_{campaign_code}` | `campaign_code = {campaign_code}` | Attributes campaign first-touch only. Does not start any test. Sends `start_without_test_text`. |
| `t.me/bot?start=k_{token}` | `link_token = {token}` | **Campaign Link Rotation:** Attributes campaign + starts the test bound to this token (if active). |
| `t.me/bot?start=` (Empty) | None | Sends `start_without_test_text`. If `enable_default_test_on_start` is on and a default test exists, starts the default test. |

---

## 2. The Question Loop (Clean Quiz Experience)

v1.6.0 renders the escaped question body inside valid `<b>...</b>`, then lists full options as numbered lines. Short inline number buttons carry the option IDs and remain server-revalidated/idempotent. Opening the same linked test again abandons the previous incomplete session and starts fresh without a resume dialog. Admin accounts follow this same student path when they explicitly open a test/campaign payload.

When one test has `secondary_enabled`, bare `/start` and links bound to that test open secondary regardless of default-test. Secondary uses its dedicated persistent keyboard (`استارت`, `راهنمایی`, `پشتیبانی`). Multistage OFF sends category 1 only; ON sends one category per click with exact next labels and final soon text. Links to other tests remove this keyboard and run only their quiz. Secondary browsing creates no quiz completion.


Once a test starts:
1. **Keyboard Cleanup:** A temporary `remove_keyboard` message is sent and immediately deleted, ensuring any leftover bottom ReplyKeyboardMarkup from older attempts or other bots is fully cleared.
2. **First Question:** The first question is sent as a new message.
3. **Question Layout:**
   - **Progress Line:** "سوال n از m" (if `question_progress_enabled` is on).
   - **Progress Bar:** A text-based visual progress bar like `[████░░░] 57%` (if `student_progress_bar_enabled` is on).
   - **Question Text:** The question text.
   - **Inline Options:** Options are rendered as **InlineKeyboardMarkup (glass buttons)**, one option per row, to maintain minimum width and look extremely native and clean.
4. **Idempotent Answer Processing:**
   - When an option is tapped, the callback carrying `"opt:<question_id>:<option_id>"` is processed.
   - **Atomic State Claim:** `ProcessedCallbackRepository::tryClaim` claims the callback query ID, and `claimQuestionForAnswer` atomically clears `current_question_id` for the session.
   - **Race Prevention:** Duplicate webhook deliveries or rapid double-taps are caught and safely ignored, preventing double-scoring or double-advancement.
5. **In-Place Question Transition:**
   - Tapping an option edits the *same* question message in place (`editMessageText`) to render the next question.
   - This keeps the chat completely clean—there is at most **one** active question message visible to the student at any time during the test.
   - Stale or previous question button taps are rejected.

---

## 3. Results Selection & Delivery

When the final question is answered:
1. **Scoring Evaluation:** `ProfileScoreEngine` sums option scores into per-profile totals.
2. **Winner Selection (`WinnerSelector`):**
   - **Single Mode:** The profile with the highest score is the primary winner. Exact ties are resolved with a random choice (logged in meta).
   - **Dual on Close Mode:** If the score gap between the first and second profiles is `<= threshold`, **both** profiles are returned. Otherwise, only the first is returned.

---

## 4. STRICT Result Content Ordering & CTA Rules

Results are delivered as a consolidated package. To ensure a professional marketing presentation, the content is strictly ordered and formatted:

### A. Ordering (F5 Rule)
1. **Text Analysis:** The description of the results (if non-empty) is sent first.
2. **Media Attachments:** Attached files (Image, Video, PDF, Document) are sent second.
3. **Voice Note LAST:** If a voice note is attached to the result, it is sent **strictly last** in the sequence.

### B. CTA Button Placement Priority
* **Glass Button Only:** The Call-To-Action (CTA) is an inline URL button attached directly to a result message.
* **No Plain-Text CTA:** The CTA button label or link is **never** repeated as plain text inside any message bubble (deleting the old duplicate text bug).
* **Only One CTA Button:** The CTA button appears **only once** on the very last content message of the delivery sequence.
* **Placement Priority Matrix:**
  1. If a **voice note** is sent (since it's last), the CTA inline keyboard is attached to the **voice note message**.
  2. Else, if a **file/document/image** is sent, the CTA inline keyboard is attached to the **file message**.
  3. Else, the CTA inline keyboard is attached to the **text analysis message**.

---

## 5. Post-Funnel Silence Policy

* **No Footer/Tutorial Spam:** After the result and its CTA are delivered, the bot is **completely silent**.
* **Forbidden Messages:** It is strictly forbidden to send a trailing helper message like "برای استفاده مجدد /start یا /help را بزنید". The student's journey is complete after the CTA.
* **Garbage Input Handling:** If the student types anything after completing the quiz, the bot ignores it or sends a single neutral line, never prompting them to restart.
