# FinanceGPT Bot

سرویس Python خالص برای ربات تلگرام، که روی **API تلگرام** اجرا می‌شود و کار
اصلی‌اش **ارسال قیمت لحظه‌ای ارز** و **محاسبه ارزش مقدار درخواستی** است؛
علاوه بر این، بررسی عضویت کانال، افزودن ربات به گروه‌های دیگران، و ثبت
لینک رفرال اختصاصی هر گروه را هم پشتیبانی می‌کند.

## معماری

```
financegpt_bot/
├── app.py              # نقطه ورود (وب‌هوک): سرور Flask روی POST /telegram
├── poller.py           # نقطه ورود (long polling): دریافت آپدیت با getUpdates
├── config.py           # پیکربندی از طریق متغیرهای محیطی
├── messages.py         # متن/کیبورد پیام‌ها
├── setup_webhook.py    # ثبت آدرس وب‌هوک نزد تلگرام (اجرای یک‌باره)
├── core/
│   ├── router.py       # درخت تصمیم‌گیری (چت خصوصی / گروه / my_chat_member)
│   └── handlers.py     # اکشن‌ها: عضویت، قیمت، افزودن به گروه، لینک رفرال
└── services/
    ├── telegram_api.py     # کلاینت Bot API تلگرام (sendMessage و ...)
    ├── price_service.py    # قیمت ارز از نوبیتکس/بیت‌پین/گیوکس
    ├── group_links.py      # لینک رفرال اختصاصی هر گروه (JSON محلی)
    └── referral_state.py   # وضعیت موقت «منتظر لینک رفرال از فلان ادمین»
```

## جریان رفتاری

- **چت خصوصی:**
  - `/start` → بررسی عضویت در کانال (`GEEVEX_CHANNEL_ID`).
    - موفق → پیام تایید + متن آموزشی امکانات + دکمه‌ی «➕ افزودن ربات به گروه».
    - ناموفق → پیام «باید عضو کانال باشید» + دکمه‌ی عضویت + دکمه‌ی «عضو شدم ✔️».
  - دکمه‌ی «➕ افزودن ربات به گروه» → پیام راهنما با دکمه‌ی شیشه‌ای لینک
    `https://t.me/<BOT_USERNAME>?startgroup=true` (لینک رسمی تلگرام برای
    انتخاب گروه و افزودن ربات، بدون نیاز به کد سمت سرور).
  - هر متن دیگر (وقتی منتظر لینک رفرال نیستیم) → قیمت لحظه‌ای یا محاسبه‌ی ارزش.
- **گروه/سوپرگروه (وقتی `GROUP_PRICE_ENABLED=true`):** متن‌هایی که نام/نماد
  یک ارز شناخته‌شده دارند → قیمت فشرده به‌صورت Reply. بقیه‌ی پیام‌ها نادیده
  گرفته می‌شوند.
- **آپدیت `my_chat_member`** (ربات به گروهی اضافه/حذف شد): وقتی ربات تازه به
  یک گروه اضافه می‌شود:
  1. با `getChatMember` بررسی می‌شود که آیا اضافه‌کننده ادمین/سازنده‌ی همان
     گروه است.
  2. اگر نبود → پیام «شما ادمین این گروه نیستید».
  3. اگر بود → تلاش برای پیام خصوصی (DM) و درخواست لینک رفرال؛ اگر DM ممکن
     نبود (چون قبلاً `/start` نزده)، همان درخواست داخل خود گروه پست می‌شود.
  4. اولین پیامِ معتبر (شروع‌شونده با `http://`/`https://`) از همان فرد، در
     همان چت، به‌عنوان لینک رفرال آن گروه ذخیره می‌شود (`services/group_links.py`).
- **دکمه‌ی «خرید»** زیر هر پیام قیمت/محاسبه: اگر گروه لینک رفرال ثبت‌شده
  داشته باشد از همان استفاده می‌شود، وگرنه از `BUY_COIN_URL` پیش‌فرض.

## قیمت لحظه‌ای ارز (نوبیتکس / بیت‌پین / گیوکس)

با ارسال نام یا نماد یک ارز (مثلاً `BTC`، `ETH`، `تتر`، یا حتی جمله‌ی «قیمت
بیت کوین چنده؟») — چه در چت خصوصی و چه در گروه — ربات قیمت تومانی و تتری آن
ارز را از هر سه صرافی نوبیتکس، بیت‌پین و گیوکس می‌گیرد و در یک پیام نمایش
می‌دهد. اگر متن شامل یک عدد باشد، به‌جای قیمت واحد، ارزش همان مقدار محاسبه و
نمایش داده می‌شود. منطق کامل در `services/price_service.py` است.

- **کش کوتاه‌مدت:** هر سه API یک‌بار در هر `PRICE_CACHE_TTL_SECONDS` (پیش‌فرض
  ۸ ثانیه) فراخوانی می‌شوند و پاسخ کامل (همه‌ی ارزها) کش می‌شود.
- **تحمل خطا:** اگر یک صرافی timeout بدهد یا خطا برگرداند، فقط همان بخش از
  پیام حذف می‌شود و دو صرافی دیگر همچنان نمایش داده می‌شوند.
- **timeout:** `PRICE_HTTP_TIMEOUT` (پیش‌فرض ۶ ثانیه) روی هر سه درخواست.
- **پاسخ در گروه:** کوتاه‌تر (یک خط برای هر صرافی) و همیشه به‌صورت Reply روی
  همان پیام کاربر ارسال می‌شود.

> **نکته‌ی مهم درباره‌ی BotFather (Privacy Mode):** به‌صورت پیش‌فرض، حالت
> Privacy تلگرام برای بات‌ها فعال است و در گروه‌ها فقط پیام‌هایی که با `/`
> شروع شوند، ربات را mention کنند، یا Reply به پیام ربات باشند به وب‌هوک/
> getUpdates می‌رسند. برای این‌که ربات بتواند **همه‌ی** پیام‌های متنی گروه را
> ببیند، باید در گفتگو با **@BotFather** دستور `/mybots` → انتخاب ربات →
> `Bot Settings` → `Group Privacy` → `Turn off` را اجرا کنید. این تنظیم فقط
> روی گروه‌هایی اثر دارد که ربات *بعد از* خاموش‌کردن Privacy دوباره به آن‌ها
> اضافه شود (یا حداقل یک‌بار admin شود). آپدیت `my_chat_member` (تشخیص افزودن
> به گروه) از این محدودیت مستثناست و همیشه می‌رسد.

## اجرا

نصب و پیکربندی:

```bash
pip install -r requirements.txt
cp .env.example .env        # حداقل TELEGRAM_BOT_TOKEN و BOT_USERNAME را پر کنید
```

دریافت پیام از تلگرام، دو حالت — **این دو حالت متقابلاً منحصربه‌فردند و هرگز
نباید هم‌زمان روی یک هاست فعال باشند** (چون `poller.py` هنگام شروع، webhook را
پاک می‌کند و باعث تداخل با `app.py` می‌شود؛ برای همین `poller.py` به‌طور
پیش‌فرض غیرفعال است و فقط با `ALLOW_POLLING=true` اجرا می‌شود):

**الف) Long polling** (بدون نیاز به URL عمومی — فقط برای تست محلی/dev):

```bash
ALLOW_POLLING=true python -m financegpt_bot.poller
```

> روی هر هاستی که `app.py`/`passenger_wsgi.py` (وب‌هوک) در تولید فعال است،
> `ALLOW_POLLING` باید `false`/خالی بماند (مقدار پیش‌فرض).

**ب) Webhook** (نیازمند آدرس عمومی HTTPS — روش تولید/production):

```bash
python -m financegpt_bot.setup_webhook   # آدرس WEBHOOK_URL را نزد تلگرام ثبت می‌کند
python -m financegpt_bot.app             # سرور وب‌هوک روی POST /telegram
```

تست‌های رفتاری:

```bash
python -m pytest tests/ -q
```

## داده‌ها

- `data/group_links.json` — لینک رفرال ثبت‌شده‌ی هر گروه (`{chat_id: لینک}`).
- `data/referral_wait_state.json` — وضعیت موقت «منتظر لینک رفرال هستیم»
  (حداکثر ۱۵ دقیقه معتبر، سپس منقضی می‌شود).
