initializare
This commit is contained in:
32
.env.example
Normal file
32
.env.example
Normal file
@@ -0,0 +1,32 @@
|
||||
# Credentials (NU comite valorile reale în git!)
|
||||
BTGO_USERNAME=
|
||||
BTGO_PASSWORD=
|
||||
|
||||
# Configurare
|
||||
HEADLESS=false
|
||||
TIMEOUT_2FA_SECONDS=120
|
||||
SCREENSHOT_ON_ERROR=true
|
||||
DOWNLOAD_TRANSACTIONS=true
|
||||
OUTPUT_DIR=./data
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# Notificări (opțional)
|
||||
ENABLE_NOTIFICATIONS=false
|
||||
|
||||
# Email (SMTP)
|
||||
EMAIL_ENABLED=false
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your-email@gmail.com
|
||||
SMTP_PASSWORD=your-app-password
|
||||
EMAIL_FROM=your-email@gmail.com
|
||||
EMAIL_TO=mmarius28@gmail.com
|
||||
|
||||
# Telegram
|
||||
TELEGRAM_ENABLED=false
|
||||
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
|
||||
TELEGRAM_CHAT_ID=your-chat-id
|
||||
|
||||
# Telegram Trigger Bot (pentru declanșare remote prin Telegram)
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789,987654321 # User IDs autorizați (goală = toți)
|
||||
TELEGRAM_POLL_TIMEOUT=60 # Long polling timeout în secunde (30-90 recomandat)
|
||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Playwright
|
||||
.pytest_cache/
|
||||
test-results/
|
||||
|
||||
# Environment & Secrets
|
||||
.env
|
||||
auth.json
|
||||
|
||||
# Data & Logs
|
||||
data/*.csv
|
||||
data/*.json
|
||||
data/*.png
|
||||
logs/*.log
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
113
CLAUDE.md
Normal file
113
CLAUDE.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
BTGO Scraper - Playwright automation for extracting account balances and transaction CSVs from Banca Transilvania George (btgo.ro).
|
||||
|
||||
**Security Context**: Authorized personal banking automation tool for educational purposes.
|
||||
|
||||
**⚠️ CRITICAL**: Docker/headless mode is **BLOCKED by WAF**. ONLY works locally with `HEADLESS=false`.
|
||||
|
||||
## Running the Scraper
|
||||
|
||||
```powershell
|
||||
# Windows setup
|
||||
deployment\windows\scripts\setup_dev.ps1
|
||||
|
||||
# Run scraper (browser visible)
|
||||
deployment\windows\scripts\run_scraper.ps1
|
||||
|
||||
# Run Telegram bot (manual)
|
||||
deployment\windows\scripts\run_telegram_bot_manual.ps1
|
||||
|
||||
# Interactive menu (all options)
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Flow
|
||||
```
|
||||
login() → handle_2fa_wait() → read_accounts() → download_transactions() → save_results()
|
||||
```
|
||||
|
||||
### Critical Implementation Details
|
||||
|
||||
#### 1. Login with Popup (lines ~140-200)
|
||||
- Main page → Click "LOGIN" → **Opens popup window**
|
||||
- Use `page.expect_popup()` to capture popup reference
|
||||
- All operations happen in `self.login_page` (the popup)
|
||||
|
||||
#### 2. 2FA Auto-Detection (lines ~202-240)
|
||||
- URL monitoring: `login.bancatransilvania.ro` → `goapp.bancatransilvania.ro`
|
||||
- Poll for `#accountsBtn` element every 2 seconds
|
||||
- After 2FA: `self.page = self.login_page` (switch page reference)
|
||||
|
||||
#### 3. Account Reading (lines ~242-310)
|
||||
- Elements: `fba-account-details-card`, `#accountsBtn`
|
||||
- Each card: `h4` (name), `span.text-grayscale-label` (IBAN), `strong.sold` (balance)
|
||||
- Balance format: `"7,223.26 RON"` → parse to float + currency
|
||||
|
||||
#### 4. Transaction Download Modal (lines ~312-420)
|
||||
|
||||
**State Machine:**
|
||||
```
|
||||
Account 1: Expand card → Click "Tranzacții" → Download → Back → Modal
|
||||
Account 2+: Select from modal → [ALREADY on page] → Download → Back
|
||||
```
|
||||
|
||||
**Critical**: After modal selection, you're ALREADY on transactions page. Don't expand/click again.
|
||||
|
||||
**Modal selectors:**
|
||||
- Modal: `.modal-content`
|
||||
- Account buttons: `#accountC14RONCRT{last_10_iban_digits}`
|
||||
- Example: IBAN `...0637236701` → `#accountC14RONCRT0637236701`
|
||||
|
||||
### Key Selectors
|
||||
|
||||
- Login: `get_by_placeholder("ID de logare")`, `get_by_placeholder("Parola")`
|
||||
- Post-login: `#accountsBtn`, `goapp.bancatransilvania.ro` domain
|
||||
- Accounts: `fba-account-details-card`, `.collapse-account-btn`, `.account-transactions-btn`
|
||||
- Modal: `.modal-content`, `#accountC14RONCRT{iban_digits}`
|
||||
- CSV: `get_by_role("button", name="CSV")`
|
||||
|
||||
**Update selectors:** `playwright codegen https://btgo.ro --target python`
|
||||
|
||||
## Docker Limitation
|
||||
|
||||
**Docker DOES NOT WORK** - Akamai WAF blocks with "Access Denied".
|
||||
|
||||
**Symptoms:**
|
||||
- "Access Denied" page
|
||||
- 0 links detected
|
||||
- Screenshot: `data/debug_before_login_*.png` shows error
|
||||
|
||||
**Solution:** Use Windows local mode with `HEADLESS=false`.
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Access Denied (Docker)
|
||||
- **Cause**: WAF blocking
|
||||
- **Fix**: Run locally with `HEADLESS=false`
|
||||
|
||||
### Transaction Download Timeout
|
||||
- Modal not detected: Check `.modal-content` selector
|
||||
- Account ID mismatch: Verify IBAN mapping `#accountC14RONCRT{last_10_digits}`
|
||||
- For idx > 1: Already on page, don't expand/click
|
||||
|
||||
### 2FA Timeout
|
||||
- Increase `TIMEOUT_2FA_SECONDS` in `.env`
|
||||
- Verify URL redirect to `goapp.bancatransilvania.ro`
|
||||
|
||||
### "Nu exista card la pozitia X"
|
||||
- Trying to access cards in modal context
|
||||
- First account needs expand, subsequent accounts don't
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: Success
|
||||
- `1`: General error
|
||||
- `4`: Config error (.env invalid)
|
||||
- `99`: Unexpected error
|
||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
# Folosim imaginea oficiala Microsoft Playwright cu Python
|
||||
FROM mcr.microsoft.com/playwright/python:v1.48.0-noble
|
||||
|
||||
# Setare working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copiaza fisierul de requirements
|
||||
COPY requirements.txt .
|
||||
|
||||
# Instaleaza dependente Python
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copiaza codul aplicatiei
|
||||
COPY btgo_scraper.py config.py ./
|
||||
|
||||
# Creaza directoare pentru output si logs
|
||||
RUN mkdir -p /app/data /app/logs
|
||||
|
||||
# Setare variabile de mediu pentru mod headless
|
||||
ENV HEADLESS=true
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
# Setare timezone (optional)
|
||||
ENV TZ=Europe/Bucharest
|
||||
|
||||
# Ruleaza script-ul principal
|
||||
CMD ["python", "btgo_scraper.py"]
|
||||
338
README.md
Normal file
338
README.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# BTGO Scraper - Automatizare Citire Solduri
|
||||
|
||||
## Prezentare
|
||||
|
||||
Automatizare Playwright pentru extragerea soldurilor și tranzacțiilor din George (btgo.ro).
|
||||
|
||||
## Arhitectură
|
||||
|
||||
### BTGO Scraper (btgo_scraper.py)
|
||||
|
||||
```
|
||||
Entry point: btgo_scraper.py
|
||||
│
|
||||
├─ config.py (Config.validate())
|
||||
│ └─ .env (load credentials)
|
||||
│
|
||||
├─ sync_playwright() → Chromium browser
|
||||
│
|
||||
├─ BTGOScraper.run()
|
||||
│ ├─ login()
|
||||
│ │ ├─ page.goto('go.bancatransilvania.ro')
|
||||
│ │ ├─ page.expect_popup() → self.login_page
|
||||
│ │ └─ Fill credentials + submit
|
||||
│ │
|
||||
│ ├─ handle_2fa_wait()
|
||||
│ │ └─ Poll URL + #accountsBtn (120s)
|
||||
│ │
|
||||
│ ├─ read_accounts()
|
||||
│ │ ├─ Click #accountsBtn
|
||||
│ │ ├─ Iterate fba-account-details-card
|
||||
│ │ └─ Extract: h4, IBAN, strong.sold
|
||||
│ │
|
||||
│ ├─ download_transactions() [optional]
|
||||
│ │ ├─ For each account:
|
||||
│ │ │ ├─ Expand card (idx=1 only)
|
||||
│ │ │ ├─ Click "Tranzacții" (idx=1) / Select from modal (idx>1)
|
||||
│ │ │ ├─ Click CSV button
|
||||
│ │ │ └─ page.expect_download()
|
||||
│ │ └─ Save to data/tranzactii_*.csv
|
||||
│ │
|
||||
│ ├─ save_results()
|
||||
│ │ ├─ Write data/solduri_*.csv
|
||||
│ │ └─ Write data/solduri_*.json
|
||||
│ │
|
||||
│ └─ send_notifications() [optional]
|
||||
│ └─ notifications.py
|
||||
│ ├─ EmailNotifier.send()
|
||||
│ │ └─ SMTP (smtplib)
|
||||
│ └─ TelegramNotifier.send()
|
||||
│ └─ requests.post(telegram API)
|
||||
│
|
||||
Standalone scripts:
|
||||
│
|
||||
├─ send_notifications.py (manual notification send)
|
||||
├─ get_telegram_chat_id.py (telegram setup helper)
|
||||
└─ test_telegram.py (telegram connectivity test)
|
||||
```
|
||||
|
||||
### Telegram Trigger Bot (telegram_trigger_bot.py)
|
||||
|
||||
```
|
||||
Entry point: telegram_trigger_bot.py
|
||||
│
|
||||
├─ .env (TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USER_IDS)
|
||||
│
|
||||
├─ TelegramTriggerBot.__init__()
|
||||
│ ├─ Load config from .env
|
||||
│ ├─ Parse allowed user IDs
|
||||
│ └─ Initialize Telegram API (api.telegram.org)
|
||||
│
|
||||
├─ run()
|
||||
│ ├─ send_message(startup notification)
|
||||
│ └─ While True:
|
||||
│ ├─ get_updates(long_polling timeout=60s)
|
||||
│ ├─ Parse incoming messages
|
||||
│ └─ handle_command()
|
||||
│ │
|
||||
│ ├─ /scrape command:
|
||||
│ │ ├─ is_user_allowed(user_id)
|
||||
│ │ ├─ send_message("🤖 Scraper pornit...")
|
||||
│ │ ├─ subprocess.run(['python', 'btgo_scraper.py'])
|
||||
│ │ │ └─ btgo_scraper.py (full execution)
|
||||
│ │ └─ send_message("✅ Succes" / "❌ Eroare")
|
||||
│ │
|
||||
│ ├─ /status command:
|
||||
│ │ └─ send_message(bot uptime info)
|
||||
│ │
|
||||
│ └─ /help command:
|
||||
│ └─ send_message(available commands)
|
||||
│
|
||||
Windows Service:
|
||||
│
|
||||
└─ deployment/windows/nssm/
|
||||
├─ BTGOTelegramBot (Windows Service)
|
||||
│ ├─ nssm install BTGOTelegramBot
|
||||
│ ├─ Auto-restart on failure
|
||||
│ └─ Log: logs/telegram_bot_*.log
|
||||
│
|
||||
Deploy scripts:
|
||||
├─ deploy.ps1 (full deployment)
|
||||
├─ install_service.ps1
|
||||
├─ start_service.ps1
|
||||
├─ restart_service.ps1
|
||||
├─ uninstall_service.ps1
|
||||
└─ menu.ps1 (interactive management)
|
||||
```
|
||||
|
||||
**Puncte cheie depanare:**
|
||||
- **Scraper login fails**: Check selectors în btgo_scraper.py:159-200
|
||||
- **Scraper 2FA timeout**: Check URL polling în btgo_scraper.py:202-240
|
||||
- **Scraper account reading fails**: Check selectors în btgo_scraper.py:242-310
|
||||
- **Scraper transaction download timeout**: Check modal logic în btgo_scraper.py:312-420
|
||||
- **Bot commands not working**: Check TELEGRAM_ALLOWED_USER_IDS în .env
|
||||
- **Bot unauthorized**: User ID not in allowed list
|
||||
- **Notifications fail**: Check notifications.py + config .env
|
||||
|
||||
**Funcționalități:**
|
||||
- ✅ Citire automată solduri pentru toate conturile
|
||||
- ✅ Export CSV cu tranzacții pentru fiecare cont
|
||||
- ✅ Autentificare 2FA automată (auto-detect)
|
||||
- ✅ Notificări Email și Telegram cu fișierele generate
|
||||
|
||||
**⚠️ LIMITARE: Docker/headless NU funcționează** - WAF-ul site-ului blochează activ containere Docker.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Cont BTGO activ cu 2FA
|
||||
- Windows (Docker nu este suportat)
|
||||
|
||||
## Setup și Rulare
|
||||
|
||||
### Setup Automat (RECOMANDAT)
|
||||
|
||||
```powershell
|
||||
# PowerShell (recomandat)
|
||||
deployment\windows\scripts\setup_dev.ps1
|
||||
|
||||
# SAU meniu interactiv complet
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
### Rulare Manuală
|
||||
|
||||
```powershell
|
||||
# Scraper direct
|
||||
deployment\windows\scripts\run_scraper.ps1
|
||||
|
||||
# Telegram bot (manual mode)
|
||||
deployment\windows\scripts\run_telegram_bot_manual.ps1
|
||||
|
||||
# SAU meniu interactiv
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
**Ce se întâmplă:**
|
||||
1. Browser Chrome se deschide vizibil
|
||||
2. Login automat
|
||||
3. Așteaptă aprobare 2FA pe telefon (120s timeout)
|
||||
4. Citește soldurile tuturor conturilor
|
||||
5. Descarcă CSV-uri cu tranzacții
|
||||
6. Salvează rezultate în `data/`
|
||||
|
||||
### Configurare `.env`
|
||||
|
||||
```bash
|
||||
# Obligatorii
|
||||
BTGO_USERNAME=your_username
|
||||
BTGO_PASSWORD=your_password
|
||||
HEADLESS=false # OBLIGATORIU false!
|
||||
|
||||
# Opționale
|
||||
DOWNLOAD_TRANSACTIONS=true
|
||||
TIMEOUT_2FA_SECONDS=120
|
||||
```
|
||||
|
||||
## Structură Output
|
||||
|
||||
```
|
||||
data/
|
||||
├── solduri_2025-11-06_14-30-45.csv # Rezumat solduri
|
||||
├── solduri_2025-11-06_14-30-45.json # Metadata + solduri
|
||||
├── tranzactii_Colector_01_2025-11-06.csv # Tranzacții per cont
|
||||
└── dashboard_2025-11-06_14-30-45.png # Screenshot
|
||||
|
||||
logs/
|
||||
└── scraper_2025-11-06.log # Log zilnic
|
||||
```
|
||||
|
||||
### Format CSV Solduri
|
||||
|
||||
```csv
|
||||
timestamp,nume_cont,iban,sold,moneda,tip_cont
|
||||
2025-11-06 14:30:45,Colector 01,RO32BTRLRONCRT0637236701,7223.26,RON,colector
|
||||
```
|
||||
|
||||
## Notificări (Email & Telegram)
|
||||
|
||||
### Email (SMTP)
|
||||
|
||||
```bash
|
||||
# .env
|
||||
ENABLE_NOTIFICATIONS=true
|
||||
EMAIL_ENABLED=true
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your-email@gmail.com
|
||||
SMTP_PASSWORD=your-app-password # Pentru Gmail: https://myaccount.google.com/apppasswords
|
||||
EMAIL_FROM=your-email@gmail.com
|
||||
EMAIL_TO=recipient@gmail.com
|
||||
```
|
||||
|
||||
### Telegram
|
||||
|
||||
1. **Crează Bot:**
|
||||
- Caută @BotFather în Telegram
|
||||
- Trimite `/newbot`
|
||||
- Copiază token-ul primit
|
||||
|
||||
2. **Obține Chat ID:**
|
||||
```bash
|
||||
python get_telegram_chat_id.py
|
||||
```
|
||||
|
||||
3. **Configurare .env:**
|
||||
```bash
|
||||
ENABLE_NOTIFICATIONS=true
|
||||
TELEGRAM_ENABLED=true
|
||||
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrs
|
||||
TELEGRAM_CHAT_ID=-1001234567890 # Negativ pentru grupuri
|
||||
```
|
||||
|
||||
### Trimitere Manuală Notificări
|
||||
|
||||
```bash
|
||||
python send_notifications.py
|
||||
```
|
||||
|
||||
## Telegram Trigger Bot - Declanșare Remote
|
||||
|
||||
### Setup Rapid (Manual Mode)
|
||||
|
||||
1. **Configurare .env:**
|
||||
```bash
|
||||
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrs
|
||||
TELEGRAM_ALLOWED_USER_IDS=your_user_id
|
||||
TELEGRAM_CHAT_ID=-1001234567890 # Pentru grup (negativ!)
|
||||
```
|
||||
|
||||
2. **Pornire bot:**
|
||||
```powershell
|
||||
deployment\windows\scripts\run_telegram_bot_manual.ps1
|
||||
```
|
||||
|
||||
3. **Comenzi disponibile:**
|
||||
- `/scrape` - Execută BTGO scraper
|
||||
- `/status` - Status bot
|
||||
- `/help` - Ajutor
|
||||
|
||||
### Deploy ca Windows Service (RECOMANDAT pentru VM)
|
||||
|
||||
```powershell
|
||||
# Deploy complet + instalare serviciu
|
||||
deployment\windows\scripts\deploy.ps1
|
||||
|
||||
# SAU folosește meniul interactiv
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
**Avantaje serviciu:**
|
||||
- ✅ Pornire automată cu Windows
|
||||
- ✅ Auto-restart la crash
|
||||
- ✅ Rulează în background (fără RDP activ)
|
||||
- ✅ Logging centralizat
|
||||
|
||||
**Notă:** Fișierele sunt trimise de **scraper** prin notificări (dacă `ENABLE_NOTIFICATIONS=true`), NU de bot.
|
||||
|
||||
**⚠️ Cerință pentru scraper:** Desktop Windows activ (RDP conectată) - browser TREBUIE vizibil!
|
||||
|
||||
**Ghid complet:** `deployment/windows/README.md` și `TELEGRAM_BOT_SETUP.md`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Access Denied" în Docker
|
||||
**Cauză:** WAF-ul BT George blochează Docker/headless browsers.
|
||||
**Soluție:** Rulează DOAR local în Windows cu `HEADLESS=false`.
|
||||
|
||||
### Timeout la 2FA
|
||||
- Mărește timeout: `TIMEOUT_2FA_SECONDS=180` în `.env`
|
||||
- Verifică notificări activate pe telefon
|
||||
|
||||
### Selectors nu funcționează
|
||||
Site-ul s-a schimbat. Re-generează selectors:
|
||||
```bash
|
||||
.venv\Scripts\activate
|
||||
playwright codegen https://btgo.ro --target python
|
||||
```
|
||||
|
||||
### Notificări Email nu funcționează
|
||||
- Pentru Gmail: folosește App Password, nu parola normală
|
||||
- Verifică port: 587 (TLS) sau 465 (SSL)
|
||||
- Testare: `python test_telegram.py`
|
||||
|
||||
### Notificări Telegram nu funcționează
|
||||
- Verifică `TELEGRAM_ENABLED=true`
|
||||
- Rulează `python test_telegram.py`
|
||||
- Chat ID pentru grupuri trebuie să fie **negativ** (ex: `-1001234567890`)
|
||||
- Asigură-te că botul este în grup
|
||||
|
||||
## Securitate
|
||||
|
||||
**⚠️ IMPORTANT:**
|
||||
- NU comite `.env` în git (deja în `.gitignore`)
|
||||
- NU partaja screenshots/logs - conțin date sensibile
|
||||
- Șterge fișierele vechi periodic:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
del /q data\*.csv data\*.json data\*.png
|
||||
|
||||
# Linux/Mac
|
||||
rm data/*.{csv,json,png}
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - Success
|
||||
- `1` - Eroare generală
|
||||
- `4` - Eroare configurare (.env invalid)
|
||||
- `99` - Eroare neașteptată
|
||||
|
||||
## Licență
|
||||
|
||||
MIT License
|
||||
|
||||
## Disclaimer Legal
|
||||
|
||||
Acest tool accesează informații personale bancare. Utilizatorul este singurul responsabil pentru conformarea cu termenii Băncii Transilvania și legislația aplicabilă.
|
||||
380
TELEGRAM_BOT_SETUP.md
Normal file
380
TELEGRAM_BOT_SETUP.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# Telegram Trigger Bot - Setup Guide
|
||||
|
||||
## Prezentare
|
||||
|
||||
Telegram Trigger Bot permite declanșarea BTGO Scraper-ului de la distanță prin comenzi Telegram. Perfect pentru VM Windows 11.
|
||||
|
||||
**⚠️ IMPORTANT:** VM-ul Windows 11 TREBUIE să aibă:
|
||||
- Remote Desktop session activă (sau user logat)
|
||||
- Browser vizibil (HEADLESS=false)
|
||||
- WAF-ul BT blochează browsere headless!
|
||||
|
||||
## Cum Funcționează
|
||||
|
||||
```
|
||||
Tu (Telegram) → Bot → Rulează scraper → Trimite CSV-uri înapoi
|
||||
```
|
||||
|
||||
## Setup Pas cu Pas
|
||||
|
||||
### 1. Configurare Bot Telegram
|
||||
|
||||
**Dacă ai deja bot pentru notificări:** Poți folosi același bot!
|
||||
|
||||
**Dacă NU ai bot:**
|
||||
1. Deschide Telegram și caută **@BotFather**
|
||||
2. Trimite `/newbot`
|
||||
3. Alege nume pentru bot (ex: "BTGO Scraper Bot")
|
||||
4. Copiază **token-ul** primit (ex: `123456789:ABCdefGHIjklMNOpqrs`)
|
||||
|
||||
### 2. Obține User ID-ul Tău
|
||||
|
||||
Ai nevoie de User ID pentru securitate (doar tu poți rula scraper-ul).
|
||||
|
||||
**Opțiunea A - Folosește bot existent:**
|
||||
```bash
|
||||
python get_telegram_chat_id.py
|
||||
```
|
||||
Vei vedea: `User ID: 123456789`
|
||||
|
||||
**Opțiunea B - Folosește @userinfobot:**
|
||||
1. Caută `@userinfobot` în Telegram
|
||||
2. Trimite `/start`
|
||||
3. Copiază User ID-ul
|
||||
|
||||
### 3. Setup pentru Grup Telegram (RECOMANDAT)
|
||||
|
||||
**Avantaj:** Comenzi și rezultate în același grup - mai convenabil decât mesaje directe!
|
||||
|
||||
#### 3.1. Adaugă Bot-ul în Grup
|
||||
|
||||
1. **Deschide grupul Telegram** unde vrei să trimiți comenzi
|
||||
2. **Adaugă bot-ul:**
|
||||
- Click pe numele grupului → "Add Members"
|
||||
- Caută bot-ul tău (ex: "BTGO Scraper Bot")
|
||||
- Adaugă
|
||||
|
||||
3. **Configurare Privacy Mode (important!):**
|
||||
|
||||
Bot-ul trebuie să vadă comenzile din grup. Ai 2 opțiuni:
|
||||
|
||||
**Opțiunea A - Dezactivează Privacy Mode (RECOMANDAT):**
|
||||
```
|
||||
1. Deschide @BotFather în Telegram
|
||||
2. Trimite /mybots
|
||||
3. Selectează bot-ul tău
|
||||
4. Bot Settings → Group Privacy → Turn OFF
|
||||
```
|
||||
Acum bot-ul vede toate mesajele care încep cu `/` (comenzi)
|
||||
|
||||
**Opțiunea B - Fă bot-ul Admin:**
|
||||
```
|
||||
1. În grup: Click nume grup → Administrators
|
||||
2. Add Administrator → Selectează bot-ul
|
||||
3. Permisiuni minime: nicio permisiune specială necesară
|
||||
```
|
||||
Admin bots văd toate mesajele automat.
|
||||
|
||||
#### 3.2. Obține Chat ID al Grupului
|
||||
|
||||
**Metoda 1 - Cu scriptul helper:**
|
||||
```bash
|
||||
python get_telegram_chat_id.py
|
||||
```
|
||||
- Trimite `/start` în grup
|
||||
- Script-ul va afișa: `Chat ID: -1001234567890` (NEGATIV pentru grupuri!)
|
||||
|
||||
**Metoda 2 - Manual:**
|
||||
1. Adaugă `@RawDataBot` în grupul tău temporar
|
||||
2. Bot-ul va afișa informații despre grup
|
||||
3. Copiază `chat.id` (va fi negativ, ex: `-1001234567890`)
|
||||
4. Șterge `@RawDataBot` din grup
|
||||
|
||||
### 4. Configurare .env
|
||||
|
||||
Editează `.env` și adaugă:
|
||||
|
||||
```bash
|
||||
# Bot token (același ca pentru notificări sau nou)
|
||||
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrs
|
||||
|
||||
# User IDs autorizați (separați prin virgulă)
|
||||
# DOAR acești useri pot rula /scrape din grup
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789,987654321
|
||||
|
||||
# Chat ID GRUP pentru notificări automate + răspunsuri comenzi
|
||||
# IMPORTANT: Negativ pentru grupuri! (ex: -1001234567890)
|
||||
TELEGRAM_CHAT_ID=-1001234567890
|
||||
```
|
||||
|
||||
**Securitate:**
|
||||
- `TELEGRAM_ALLOWED_USER_IDS` = doar acești useri pot rula `/scrape` din grup
|
||||
- Lasă gol dacă vrei ca oricine din grup să poată rula (nesigur!)
|
||||
- Bot-ul verifică User ID-ul celui care trimite comanda, NU group ID-ul
|
||||
|
||||
### 4. Pornire Bot
|
||||
|
||||
**Opțiunea 1 - Manual (Test):**
|
||||
```powershell
|
||||
deployment\windows\scripts\run_telegram_bot_manual.ps1
|
||||
```
|
||||
|
||||
**Opțiunea 2 - Windows Service (RECOMANDAT pentru VM):**
|
||||
```powershell
|
||||
# Deploy complet
|
||||
deployment\windows\scripts\deploy.ps1
|
||||
|
||||
# SAU meniu interactiv
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
**Opțiunea 3 - Manual Python:**
|
||||
```bash
|
||||
.venv\Scripts\activate
|
||||
python telegram_trigger_bot.py
|
||||
```
|
||||
|
||||
**Bot-ul va afișa:**
|
||||
```
|
||||
Bot inițializat. Useri autorizați: [123456789]
|
||||
Bot pornit. Așteaptă comenzi...
|
||||
```
|
||||
|
||||
## Utilizare
|
||||
|
||||
### Comenzi Disponibile
|
||||
|
||||
**În grupul Telegram (sau DM către bot):**
|
||||
|
||||
```
|
||||
/start - Pornește bot-ul și vezi comenzile
|
||||
/scrape - ⭐ Rulează scraper-ul (2-3 minute)
|
||||
/status - Vezi status sistem (ultima rulare, fișiere)
|
||||
/help - Ajutor utilizare
|
||||
```
|
||||
|
||||
**Securitate:** Doar userii din `TELEGRAM_ALLOWED_USER_IDS` pot rula comenzi!
|
||||
|
||||
### Flow Tipic în Grup
|
||||
|
||||
1. **Scrii în grup:** `/scrape`
|
||||
2. **Bot răspunde:** "🤖 BTGO Scraper pornit... Așteaptă 2FA!"
|
||||
3. **Pe VM:** Browser-ul se deschide, face login automat
|
||||
4. **Pe telefon:** Aprobă notificarea George 2FA
|
||||
5. **Scraper trimite fișiere:** Prin notificări automate (email + grup)
|
||||
6. **Bot confirmă:** "✅ Finalizat! Fișierele au fost trimise automat."
|
||||
|
||||
**Notă:** Fișierele sunt trimise de **scraper** (`ENABLE_NOTIFICATIONS=true`), NU de bot. Bot-ul doar declanșează execuția și confirmă finalizarea.
|
||||
|
||||
### Exemplu Output în Grup
|
||||
|
||||
```
|
||||
Tu: /scrape
|
||||
|
||||
Bot: 🤖 BTGO Scraper pornit...
|
||||
Așteaptă 2FA pe telefon!
|
||||
|
||||
[După 2 minute - mesaje de la scraper prin notificări]
|
||||
|
||||
Bot: 📧 BTGO Scraper - Sold conturi actualizat
|
||||
|
||||
📄 solduri_2025-11-06.csv
|
||||
📄 tranzactii_Colector_01.csv
|
||||
📄 tranzactii_Antreprenor_04.csv
|
||||
... (toate fișierele)
|
||||
|
||||
Bot: ✅ Scraper finalizat cu succes!
|
||||
📧 Fișierele au fost trimise automat prin notificări.
|
||||
```
|
||||
|
||||
**Important:** Mesajele cu fișiere vin din **scraper** (prin `notifications.py`), nu din bot! Bot-ul doar declanșează și confirmă.
|
||||
|
||||
## Setup VM Windows 11
|
||||
|
||||
### Cerințe Critice
|
||||
|
||||
**⚠️ Browser TREBUIE să fie vizibil!** WAF-ul BT blochează headless.
|
||||
|
||||
### Metoda Recomandată - Windows Service
|
||||
|
||||
```powershell
|
||||
# Deploy ca serviciu Windows (auto-start, auto-restart)
|
||||
deployment\windows\scripts\deploy.ps1
|
||||
```
|
||||
|
||||
**Avantaje:**
|
||||
- ✅ Pornire automată cu Windows
|
||||
- ✅ Auto-restart la crash
|
||||
- ✅ Management prin Services.msc
|
||||
- ✅ Logging centralizat
|
||||
- ✅ Nu necesită RDP conectată
|
||||
|
||||
**Management:**
|
||||
```powershell
|
||||
# Meniu interactiv
|
||||
deployment\windows\scripts\menu.ps1
|
||||
|
||||
# SAU comenzi directe
|
||||
deployment\windows\scripts\start_service.ps1
|
||||
deployment\windows\scripts\stop_service.ps1
|
||||
deployment\windows\scripts\restart_service.ps1
|
||||
deployment\windows\scripts\status.ps1
|
||||
```
|
||||
|
||||
### Alternativă - Task Scheduler
|
||||
|
||||
**Doar dacă nu vrei Windows Service:**
|
||||
|
||||
1. Deschide Task Scheduler
|
||||
2. Create Task
|
||||
3. **General:**
|
||||
- Name: "BTGO Telegram Bot"
|
||||
- Run only when user is logged on: ✅
|
||||
4. **Triggers:**
|
||||
- At log on (Specific user)
|
||||
5. **Actions:**
|
||||
- Action: Start a program
|
||||
- Program: `C:\path\to\btgo-playwright\deployment\windows\scripts\run_telegram_bot_manual.ps1`
|
||||
- Start in: `C:\path\to\btgo-playwright`
|
||||
6. **Conditions:**
|
||||
- Dezactivează "Start task only if computer is on AC power"
|
||||
|
||||
### Verificare Funcționare
|
||||
|
||||
```bash
|
||||
# Pe VM, verifică că bot-ul rulează
|
||||
tasklist | findstr python
|
||||
|
||||
# Ar trebui să vezi:
|
||||
# python.exe 12345 Console 1 45,678 K
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot nu răspunde la comenzi
|
||||
|
||||
**Cauză:** Token greșit sau bot nestartă.
|
||||
|
||||
**Soluție:**
|
||||
```bash
|
||||
# Verifică token-ul
|
||||
python telegram_trigger_bot.py
|
||||
|
||||
# Ar trebui să vezi:
|
||||
# Bot inițializat. Useri autorizați: [123456789]
|
||||
```
|
||||
|
||||
### "⛔ Acces interzis!"
|
||||
|
||||
**Cauză:** User ID-ul tău nu e în `TELEGRAM_ALLOWED_USER_IDS`.
|
||||
|
||||
**Soluție:**
|
||||
```bash
|
||||
# Verifică User ID-ul tău
|
||||
python get_telegram_chat_id.py
|
||||
|
||||
# Adaugă-l în .env
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789
|
||||
```
|
||||
|
||||
### "Access Denied" la scraping
|
||||
|
||||
**Cauză:** Browser-ul rulează în headless mode sau VM nu are GUI activă.
|
||||
|
||||
**Soluție:**
|
||||
- Conectează-te prin RDP și LASĂ sesiunea deschisă
|
||||
- Verifică `.env`: `HEADLESS=false`
|
||||
- Asigură-te că desktop-ul Windows este vizibil
|
||||
|
||||
### Scraper timeout (> 10 minute)
|
||||
|
||||
**Cauză:** Probabil blocat la 2FA sau browser nu răspunde.
|
||||
|
||||
**Soluție:**
|
||||
- Verifică logs pe VM: `logs\scraper_*.log`
|
||||
- Mărește timeout în bot: `timeout=900` (15 min)
|
||||
- Asigură-te că aprobați 2FA în < 2 minute
|
||||
|
||||
### Bot se oprește singur
|
||||
|
||||
**Cauză:** Sesiune RDP deconectată sau VM suspended.
|
||||
|
||||
**Soluție:**
|
||||
- Folosește Task Scheduler pentru restart automat
|
||||
- Configurează VM să nu intre în sleep/hibernate
|
||||
- Verifică că desktop-ul rămâne activ
|
||||
|
||||
## Securitate
|
||||
|
||||
**⚠️ ATENȚIE:**
|
||||
- Bot-ul are acces la credentials din `.env`
|
||||
- `TELEGRAM_ALLOWED_USER_IDS` TREBUIE configurat!
|
||||
- Nu partaja token-ul botului
|
||||
- VM-ul trebuie securizat (firewall, VPN)
|
||||
|
||||
**Best Practices:**
|
||||
```bash
|
||||
# ✅ Bun - doar tu și admin
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789,987654321
|
||||
|
||||
# ❌ Rău - oricine cu acces la bot
|
||||
TELEGRAM_ALLOWED_USER_IDS=
|
||||
|
||||
# ✅ Bun - notificări separate de trigger
|
||||
TELEGRAM_BOT_TOKEN=bot_trigger_token
|
||||
TELEGRAM_CHAT_ID=group_chat_id
|
||||
```
|
||||
|
||||
## Logs și Monitoring
|
||||
|
||||
**Logs bot:**
|
||||
```
|
||||
[2025-11-06 17:30:00] [INFO] Bot inițializat. Useri autorizați: [123456789]
|
||||
[2025-11-06 17:30:05] [INFO] Mesaj de la username (ID: 123456789): /scrape
|
||||
[2025-11-06 17:30:05] [INFO] Comandă /scrape primită
|
||||
[2025-11-06 17:30:05] [INFO] Pornire scraper...
|
||||
[2025-11-06 17:32:45] [INFO] Scraper finalizat cu succes
|
||||
[2025-11-06 17:32:50] [INFO] Trimit: solduri_2025-11-06.csv
|
||||
```
|
||||
|
||||
**Logs scraper:**
|
||||
```
|
||||
logs\scraper_2025-11-06.log
|
||||
```
|
||||
|
||||
## Alternative Trigger Methods
|
||||
|
||||
### 1. HTTP API (simplă)
|
||||
```python
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/scrape', methods=['POST'])
|
||||
def trigger_scrape():
|
||||
# Verifică token secret
|
||||
subprocess.run(['python', 'btgo_scraper.py'])
|
||||
```
|
||||
|
||||
### 2. Cron Job (periodicitate fixă)
|
||||
```
|
||||
Task Scheduler → Daily la 9:00 AM
|
||||
```
|
||||
|
||||
### 3. File Watcher
|
||||
```python
|
||||
# Detectează fișier "trigger.txt"
|
||||
# Rulează scraper automat
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
**Timpi tipici:**
|
||||
- Bot startup: < 5 secunde
|
||||
- Scraper execution: 2-3 minute
|
||||
- File upload: 5-10 secunde
|
||||
- **Total:** ~3-4 minute de la `/scrape` la fișiere primite
|
||||
|
||||
**Resource usage (VM):**
|
||||
- RAM: 500MB (bot) + 800MB (browser)
|
||||
- CPU: 5-10% idle, 30-40% during scrape
|
||||
- Network: ~5MB download (CSV-uri)
|
||||
639
btgo_scraper.py
Normal file
639
btgo_scraper.py
Normal file
@@ -0,0 +1,639 @@
|
||||
"""
|
||||
BTGO Scraper - Automatizare citire solduri conturi bancare
|
||||
Folosește Playwright pentru automatizare browser
|
||||
"""
|
||||
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout
|
||||
import logging
|
||||
import csv
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from config import Config
|
||||
|
||||
|
||||
class BTGoScraper:
|
||||
"""Scraper pentru extragerea soldurilor de pe btgo.ro"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initializare scraper cu logging si configurare"""
|
||||
self.config = Config()
|
||||
Config.validate()
|
||||
self._setup_logging()
|
||||
self._ensure_directories()
|
||||
self.page = None
|
||||
self.login_page = None # Pagina de login (popup)
|
||||
|
||||
# Setup pentru progress updates prin Telegram (optional)
|
||||
self.telegram_chat_id = os.getenv('TELEGRAM_CHAT_ID')
|
||||
self.telegram_message_id = os.getenv('TELEGRAM_MESSAGE_ID')
|
||||
self.progress_notifier = None
|
||||
|
||||
logging.info(f"Environment: TELEGRAM_CHAT_ID={self.telegram_chat_id}, TELEGRAM_MESSAGE_ID={self.telegram_message_id}")
|
||||
|
||||
# Inițializează notifier pentru progress updates dacă avem chat_id
|
||||
if self.telegram_chat_id and self.telegram_message_id:
|
||||
try:
|
||||
from notifications import TelegramNotifier
|
||||
self.progress_notifier = TelegramNotifier(self.config)
|
||||
logging.info(f"Progress updates activate pentru chat_id={self.telegram_chat_id}, message_id={self.telegram_message_id}")
|
||||
except Exception as e:
|
||||
logging.warning(f"Nu am putut inițializa progress notifier: {e}")
|
||||
else:
|
||||
logging.warning("Progress updates dezactivate - lipsesc TELEGRAM_CHAT_ID sau TELEGRAM_MESSAGE_ID")
|
||||
|
||||
def _setup_logging(self):
|
||||
"""Configurare logging in consola si fisier zilnic"""
|
||||
# Creaza director logs daca nu exista
|
||||
Path(self.config.LOG_DIR).mkdir(exist_ok=True)
|
||||
|
||||
# Nume fisier cu data curenta
|
||||
log_file = Path(self.config.LOG_DIR) / f"scraper_{datetime.now().strftime('%Y-%m-%d')}.log"
|
||||
|
||||
# Format log
|
||||
log_format = '[%(asctime)s] [%(levelname)s] %(message)s'
|
||||
date_format = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
# Configurare logging
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, self.config.LOG_LEVEL),
|
||||
format=log_format,
|
||||
datefmt=date_format,
|
||||
handlers=[
|
||||
logging.StreamHandler(), # Console
|
||||
logging.FileHandler(log_file, encoding='utf-8') # Fisier
|
||||
]
|
||||
)
|
||||
|
||||
logging.info(f"Logging initializat: {log_file}")
|
||||
|
||||
def _ensure_directories(self):
|
||||
"""Creaza directoarele data/ si logs/ daca nu exista"""
|
||||
Path(self.config.OUTPUT_DIR).mkdir(exist_ok=True)
|
||||
Path(self.config.LOG_DIR).mkdir(exist_ok=True)
|
||||
logging.info(f"Directoare verificate: {self.config.OUTPUT_DIR}, {self.config.LOG_DIR}")
|
||||
|
||||
def _update_progress(self, message: str):
|
||||
"""
|
||||
Trimite update de progres către Telegram (editează mesajul inițial)
|
||||
|
||||
Args:
|
||||
message: Mesajul de progres
|
||||
"""
|
||||
if self.progress_notifier and self.telegram_chat_id and self.telegram_message_id:
|
||||
full_message = f"*{message}*"
|
||||
self.progress_notifier._edit_message(
|
||||
self.telegram_chat_id,
|
||||
self.telegram_message_id,
|
||||
full_message
|
||||
)
|
||||
logging.info(f"Progress update: {message}")
|
||||
|
||||
def run(self):
|
||||
"""Entry point principal - orchestreaza tot flow-ul"""
|
||||
try:
|
||||
logging.info("=" * 60)
|
||||
logging.info("Start BTGO Scraper")
|
||||
logging.info("=" * 60)
|
||||
|
||||
with sync_playwright() as p:
|
||||
# Lansare browser
|
||||
browser = p.chromium.launch(
|
||||
headless=self.config.HEADLESS,
|
||||
slow_mo=100 if not self.config.HEADLESS else 0 # Slow motion pentru debugging
|
||||
)
|
||||
|
||||
# Creaza pagina cu viewport standard
|
||||
self.page = browser.new_page(viewport={'width': 1920, 'height': 1080})
|
||||
logging.info(f"Browser lansat (headless={self.config.HEADLESS})")
|
||||
|
||||
# Flow complet
|
||||
self.login()
|
||||
self.handle_2fa_wait()
|
||||
self._update_progress("2FA aprobat! Incarc conturi...")
|
||||
accounts = self.read_accounts()
|
||||
csv_path, json_path = self.save_results(accounts)
|
||||
|
||||
# Descarcă tranzacții pentru toate conturile (optional)
|
||||
downloaded_files = []
|
||||
if self.config.DOWNLOAD_TRANSACTIONS:
|
||||
downloaded_files = self.download_transactions(accounts)
|
||||
else:
|
||||
logging.info("Download tranzacții dezactivat (DOWNLOAD_TRANSACTIONS=false)")
|
||||
|
||||
# Screenshot final de confirmare
|
||||
screenshot_path = Path(self.config.OUTPUT_DIR) / f"dashboard_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.png"
|
||||
self.page.screenshot(path=str(screenshot_path))
|
||||
logging.info(f"Screenshot final salvat: {screenshot_path}")
|
||||
|
||||
# Trimite notificări (email, Discord)
|
||||
if self.config.ENABLE_NOTIFICATIONS:
|
||||
self.send_notifications(csv_path, downloaded_files, accounts)
|
||||
|
||||
# Afișează rezumat final
|
||||
logging.info("=" * 60)
|
||||
logging.info("REZUMAT FINAL")
|
||||
logging.info("=" * 60)
|
||||
logging.info(f"Conturi citite: {len(accounts)}")
|
||||
if self.config.DOWNLOAD_TRANSACTIONS:
|
||||
logging.info(f"Tranzacții descărcate: {len(downloaded_files)}")
|
||||
for df in downloaded_files:
|
||||
logging.info(f" - {df['cont']}: {df['fisier']}")
|
||||
logging.info("=" * 60)
|
||||
|
||||
browser.close()
|
||||
# Nu mai trimitem update progress aici - mesajul final cu soldurile a fost deja editat de notifications.py
|
||||
logging.info("=" * 60)
|
||||
logging.info("✓ Scraper finalizat cu succes!")
|
||||
logging.info("=" * 60)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self._handle_error(e)
|
||||
return False
|
||||
|
||||
def login(self):
|
||||
"""Autentificare cu username si password"""
|
||||
self._update_progress("Deschid pagina de login...")
|
||||
logging.info("Navigare catre https://go.bancatransilvania.ro/")
|
||||
self.page.goto('https://go.bancatransilvania.ro/', wait_until='networkidle')
|
||||
logging.info("Pagina incarcata")
|
||||
|
||||
try:
|
||||
# Cookie consent - asteapta si accepta
|
||||
logging.info("Acceptare cookies...")
|
||||
try:
|
||||
cookie_button = self.page.get_by_role("button", name="Sunt de acord", exact=True)
|
||||
cookie_button.click(timeout=5000)
|
||||
logging.info("✓ Cookies acceptate")
|
||||
except:
|
||||
logging.info("Nu a fost necesar acceptul cookies (posibil deja acceptat)")
|
||||
|
||||
# Click pe butonul LOGIN - deschide popup
|
||||
logging.info("Click pe butonul LOGIN...")
|
||||
with self.page.expect_popup() as popup_info:
|
||||
login_link = self.page.get_by_role("link", name="LOGIN")
|
||||
login_link.click()
|
||||
|
||||
# Preia referinta la popup-ul de login
|
||||
self.login_page = popup_info.value
|
||||
logging.info("✓ Popup login deschis")
|
||||
|
||||
# Completare username
|
||||
logging.info("Completare username...")
|
||||
username_field = self.login_page.get_by_placeholder("ID de logare")
|
||||
username_field.fill(self.config.BTGO_USERNAME)
|
||||
logging.info("✓ Username completat")
|
||||
|
||||
# Completare password
|
||||
logging.info("Completare password...")
|
||||
password_field = self.login_page.get_by_placeholder("Parola")
|
||||
password_field.fill(self.config.BTGO_PASSWORD)
|
||||
logging.info("✓ Password completat")
|
||||
|
||||
# Click pe butonul de submit
|
||||
logging.info("Click pe 'Mergi mai departe'...")
|
||||
submit_button = self.login_page.get_by_role("button", name="Mergi mai departe")
|
||||
submit_button.click()
|
||||
logging.info("✓ Credentials trimise, astept 2FA...")
|
||||
self._update_progress("Astept aprobare 2FA pe telefon...")
|
||||
|
||||
except PlaywrightTimeout as e:
|
||||
logging.error(f"Timeout la login: {e}")
|
||||
raise Exception("Nu am gasit elementele de login. Verifica selectors!")
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare la login: {e}")
|
||||
raise
|
||||
|
||||
def handle_2fa_wait(self):
|
||||
"""Asteapta aprobare 2FA cu auto-detect"""
|
||||
|
||||
logging.info("=" * 60)
|
||||
logging.info("🔐 APROBARE 2FA NECESARA")
|
||||
logging.info("=" * 60)
|
||||
logging.info("Verifica aplicatia George pe telefon si aproba autentificarea.")
|
||||
logging.info(f"Timeout: {self.config.TIMEOUT_2FA_SECONDS} secunde")
|
||||
logging.info("=" * 60)
|
||||
|
||||
# Detectam login reusit prin schimbarea URL-ului la goapp.bancatransilvania.ro
|
||||
# sau prin aparitia butonului de conturi (#accountsBtn)
|
||||
post_login_selectors = [
|
||||
"#accountsBtn", # Butonul pentru conturi (apare post-login)
|
||||
]
|
||||
|
||||
start_time = datetime.now()
|
||||
timeout = self.config.TIMEOUT_2FA_SECONDS
|
||||
|
||||
while (datetime.now() - start_time).seconds < timeout:
|
||||
# Verifică dacă butonul de conturi este CLICKABLE (nu doar vizibil)
|
||||
# Aceasta este singura verificare sigură - butonul apare doar după 2FA reușit
|
||||
try:
|
||||
# Așteaptă ca #accountsBtn să fie clickable (nu doar vizibil în DOM)
|
||||
accounts_btn = self.login_page.locator("#accountsBtn")
|
||||
if accounts_btn.is_visible(timeout=1000):
|
||||
# Verifică că este și clickable (enabled)
|
||||
if accounts_btn.is_enabled():
|
||||
logging.info("✓ Autentificare 2FA reusita! (Buton conturi activ)")
|
||||
time.sleep(2) # Asteapta ca pagina sa se stabilizeze complet
|
||||
# Update page reference la login_page pentru restul operatiilor
|
||||
self.page = self.login_page
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Afiseaza countdown
|
||||
elapsed = (datetime.now() - start_time).seconds
|
||||
remaining = timeout - elapsed
|
||||
print(f"\rAsteptam aprobare 2FA... {remaining}s ramas", end='', flush=True)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
print() # New line dupa countdown
|
||||
raise TimeoutError(f"Timeout 2FA dupa {timeout} secunde. Verifica ca ai aprobat pe telefon!")
|
||||
|
||||
def download_transactions(self, accounts):
|
||||
"""Descarca CSV-uri cu tranzactiile pentru fiecare cont"""
|
||||
logging.info("=" * 60)
|
||||
logging.info("Descarcare tranzactii pentru toate conturile...")
|
||||
logging.info("=" * 60)
|
||||
|
||||
downloaded_files = []
|
||||
|
||||
# IMPORTANT: Collapse toate conturile mai intai
|
||||
logging.info("Collapse toate conturile...")
|
||||
all_expanded = self.page.locator(".mat-icon.rotate-90").all()
|
||||
for expanded_icon in all_expanded:
|
||||
try:
|
||||
expanded_icon.click()
|
||||
time.sleep(0.3)
|
||||
except:
|
||||
pass
|
||||
|
||||
time.sleep(1)
|
||||
logging.info("✓ Toate conturile sunt collapse")
|
||||
|
||||
# Re-gaseste toate cardurile de conturi
|
||||
all_cards = self.page.locator("fba-account-details-card").all()
|
||||
logging.info(f"Gasit {len(all_cards)} carduri de conturi")
|
||||
|
||||
for idx, account in enumerate(accounts, 1):
|
||||
try:
|
||||
nume_cont = account['nume_cont']
|
||||
iban = account['iban']
|
||||
|
||||
self._update_progress(f"Descarc tranzactii ({idx}/{len(accounts)})...")
|
||||
logging.info(f"[{idx}/{len(accounts)}] Descarcare tranzactii pentru: {nume_cont}")
|
||||
|
||||
# Doar pentru PRIMUL cont trebuie expand + click Tranzacții
|
||||
# Pentru restul, suntem deja pe pagina de tranzacții (din selectarea din modal)
|
||||
if idx == 1:
|
||||
# Primul cont - expand și click Tranzacții
|
||||
if idx - 1 >= len(all_cards):
|
||||
logging.error(f" ✗ Nu exista card la pozitia {idx-1}")
|
||||
continue
|
||||
|
||||
card = all_cards[idx - 1]
|
||||
|
||||
# Expand contul (click pe săgeată)
|
||||
expand_button = card.locator(".collapse-account-btn").first
|
||||
expand_button.click()
|
||||
time.sleep(2) # Așteaptă expandare
|
||||
logging.info(f" Contul expandat")
|
||||
|
||||
# Click pe butonul Tranzacții
|
||||
try:
|
||||
transactions_button = card.locator(".account-transactions-btn").first
|
||||
transactions_button.click()
|
||||
time.sleep(3) # Așteaptă încărcarea paginii cu tranzacții
|
||||
logging.info(f" Click pe buton Tranzactii - pagina se incarca...")
|
||||
except Exception as e:
|
||||
logging.error(f" ✗ Nu am gasit butonul Tranzactii: {e}")
|
||||
try:
|
||||
expand_button.click()
|
||||
time.sleep(0.5)
|
||||
except:
|
||||
pass
|
||||
continue
|
||||
else:
|
||||
# Conturile 2-5: suntem deja pe pagina de tranzacții (din modal)
|
||||
logging.info(f" Deja pe pagina tranzactii (selectat din modal)")
|
||||
time.sleep(2) # Așteaptă stabilizare pagină
|
||||
|
||||
# Așteaptă să apară butonul CSV (indica că pagina s-a încărcat)
|
||||
try:
|
||||
self.page.wait_for_selector('button:has-text("CSV")', timeout=5000)
|
||||
logging.info(f" Buton CSV detectat")
|
||||
except:
|
||||
logging.warning(f" Timeout asteptand butonul CSV")
|
||||
|
||||
# Click pe butonul CSV și așteaptă download
|
||||
try:
|
||||
with self.page.expect_download(timeout=15000) as download_info:
|
||||
csv_button = self.page.get_by_role("button", name="CSV")
|
||||
csv_button.click()
|
||||
logging.info(f" Click pe butonul CSV - astept download...")
|
||||
|
||||
download = download_info.value
|
||||
|
||||
# Salvează fișierul cu un nume descriptiv
|
||||
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||
nume_safe = nume_cont.replace(' ', '_').replace('/', '_')
|
||||
filename = f"tranzactii_{nume_safe}_{timestamp}.csv"
|
||||
save_path = Path(self.config.OUTPUT_DIR) / filename
|
||||
|
||||
download.save_as(save_path)
|
||||
logging.info(f" ✓ Salvat: {save_path}")
|
||||
|
||||
downloaded_files.append({
|
||||
'cont': nume_cont,
|
||||
'iban': iban,
|
||||
'fisier': str(save_path)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f" ✗ Eroare la descarcarea CSV: {e}")
|
||||
|
||||
# Navighează înapoi la lista de conturi
|
||||
try:
|
||||
# Click pe butonul back/close (săgeată stânga sau X)
|
||||
back_button = self.page.locator('button[aria-label="Back"], .back-button, #selectAccountBtn').first
|
||||
back_button.click()
|
||||
time.sleep(1.5)
|
||||
logging.info(f" Navigat inapoi - verific modal...")
|
||||
except Exception as e:
|
||||
logging.warning(f" Nu am putut naviga inapoi: {e}")
|
||||
time.sleep(1)
|
||||
|
||||
# Verifică dacă a apărut modal de selectare cont
|
||||
try:
|
||||
modal_visible = self.page.locator('.modal-content').is_visible(timeout=2000)
|
||||
if modal_visible and idx < len(accounts):
|
||||
logging.info(f" Modal detectat - selectez contul urmator...")
|
||||
|
||||
# Calculează ID-ul contului următor
|
||||
next_account = accounts[idx] # idx este 0-indexed pentru next
|
||||
next_iban = next_account['iban']
|
||||
next_iban_digits = ''.join(filter(str.isdigit, next_iban))[-10:]
|
||||
next_account_id = f"accountC14RONCRT{next_iban_digits}"
|
||||
|
||||
# Click pe contul următor din modal
|
||||
modal_account = self.page.locator(f'#{next_account_id}').first
|
||||
modal_account.click()
|
||||
time.sleep(2)
|
||||
logging.info(f" ✓ Selectat cont din modal: {next_account['nume_cont']}")
|
||||
else:
|
||||
# Nu e modal - e ultima iteratie sau nu a aparut modal
|
||||
logging.info(f" Nu e modal - continuam normal")
|
||||
except Exception as e:
|
||||
logging.warning(f" Eroare verificare modal: {e}")
|
||||
|
||||
# Re-găsește cardurile (pentru flow normal fără modal)
|
||||
try:
|
||||
all_cards = self.page.locator("fba-account-details-card").all()
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f" ✗ Eroare la descarcarea tranzactiilor pentru {nume_cont}: {e}")
|
||||
# Încearcă să navighezi înapoi
|
||||
try:
|
||||
self.page.keyboard.press("Escape")
|
||||
time.sleep(1)
|
||||
except:
|
||||
pass
|
||||
continue
|
||||
|
||||
logging.info("=" * 60)
|
||||
logging.info(f"✓ Descarcate {len(downloaded_files)}/{len(accounts)} fisiere CSV cu tranzactii")
|
||||
logging.info("=" * 60)
|
||||
|
||||
return downloaded_files
|
||||
|
||||
def read_accounts(self):
|
||||
"""Extrage soldurile tuturor conturilor"""
|
||||
logging.info("Citire conturi si solduri...")
|
||||
|
||||
try:
|
||||
# Click pe butonul de conturi pentru a afisa lista
|
||||
logging.info("Click pe butonul Conturi...")
|
||||
# Simplu selector - doar butonul, nu div-ul interior
|
||||
accounts_button = self.page.locator("#accountsBtn")
|
||||
accounts_button.click()
|
||||
time.sleep(3) # Asteapta ca lista sa se incarce
|
||||
logging.info("✓ Sectiunea conturi deschisa")
|
||||
|
||||
# Update progres DUPĂ ce lista de conturi s-a încărcat
|
||||
self._update_progress("Citesc solduri conturi...")
|
||||
|
||||
# Gaseste toate cardurile de conturi
|
||||
account_cards = self.page.locator("fba-account-details-card").all()
|
||||
logging.info(f"Gasit {len(account_cards)} conturi")
|
||||
|
||||
accounts = []
|
||||
|
||||
for idx, card in enumerate(account_cards, 1):
|
||||
try:
|
||||
# Extrage nume cont (din tag <h4>)
|
||||
nume = card.locator("h4.fw-normal.mb-0").inner_text()
|
||||
|
||||
# Extrage IBAN (din span cu clasa specific)
|
||||
iban = card.locator("span.small.text-grayscale-label").inner_text()
|
||||
|
||||
# Extrage sold (din <strong> cu clasa 'sold')
|
||||
sold_text = card.locator("strong.sold").inner_text()
|
||||
|
||||
# Parsing sold: "7,223.26 RON" -> sold_numeric=7223.26, moneda="RON"
|
||||
# Elimina spatii, inlocuieste virgula cu punct
|
||||
sold_parts = sold_text.strip().split()
|
||||
|
||||
if len(sold_parts) >= 2:
|
||||
sold_str = sold_parts[0] # "7,223.26"
|
||||
moneda = sold_parts[1] # "RON"
|
||||
else:
|
||||
sold_str = sold_text
|
||||
moneda = "RON" # Default
|
||||
|
||||
# Converteste la float (elimina virgule care sunt separator de mii)
|
||||
# Format: "7,223.26" -> 7223.26
|
||||
sold_numeric = float(sold_str.replace(',', ''))
|
||||
|
||||
# Determina tip cont din nume (optional)
|
||||
nume_lower = nume.lower()
|
||||
if 'colector' in nume_lower:
|
||||
tip_cont = 'colector'
|
||||
elif 'profit' in nume_lower:
|
||||
tip_cont = 'profit'
|
||||
elif 'operationale' in nume_lower or 'operational' in nume_lower:
|
||||
tip_cont = 'operational'
|
||||
elif 'taxe' in nume_lower:
|
||||
tip_cont = 'taxe'
|
||||
elif 'antreprenor' in nume_lower:
|
||||
tip_cont = 'antreprenor'
|
||||
else:
|
||||
tip_cont = 'curent'
|
||||
|
||||
accounts.append({
|
||||
'nume_cont': nume.strip(),
|
||||
'iban': iban.strip(),
|
||||
'sold': sold_numeric,
|
||||
'moneda': moneda.strip(),
|
||||
'tip_cont': tip_cont
|
||||
})
|
||||
|
||||
logging.info(f" [{idx}] {nume}: {sold_numeric} {moneda}")
|
||||
|
||||
except Exception as e:
|
||||
logging.warning(f"Eroare la citirea contului {idx}: {e}")
|
||||
continue
|
||||
|
||||
if not accounts:
|
||||
raise Exception("Nu am putut citi niciun cont din lista!")
|
||||
|
||||
logging.info(f"✓ Citite {len(accounts)} conturi cu succes")
|
||||
self._update_progress(f"Gasite {len(accounts)} conturi")
|
||||
return accounts
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare la citirea conturilor: {e}")
|
||||
raise
|
||||
|
||||
def save_results(self, accounts):
|
||||
"""Salveaza rezultate in CSV si JSON"""
|
||||
if not accounts:
|
||||
logging.warning("Nu exista conturi de salvat!")
|
||||
return
|
||||
|
||||
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||
|
||||
# Salvare CSV
|
||||
csv_path = Path(self.config.OUTPUT_DIR) / f'solduri_{timestamp}.csv'
|
||||
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||||
fieldnames = ['timestamp', 'nume_cont', 'iban', 'sold', 'moneda', 'tip_cont']
|
||||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
|
||||
for account in accounts:
|
||||
row = account.copy()
|
||||
row['timestamp'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
writer.writerow(row)
|
||||
|
||||
logging.info(f"✓ CSV salvat: {csv_path}")
|
||||
|
||||
# Salvare JSON
|
||||
json_path = Path(self.config.OUTPUT_DIR) / f'solduri_{timestamp}.json'
|
||||
|
||||
# Calculeaza metadata
|
||||
currencies = list(set(a.get('moneda', 'N/A') for a in accounts))
|
||||
total_ron = sum(a['sold'] for a in accounts if a.get('moneda') == 'RON')
|
||||
|
||||
output = {
|
||||
'metadata': {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'scraper_version': '1.0.0',
|
||||
'total_accounts': len(accounts),
|
||||
'total_ron': round(total_ron, 2),
|
||||
'currencies': currencies
|
||||
},
|
||||
'accounts': accounts
|
||||
}
|
||||
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(output, f, indent=2, ensure_ascii=False)
|
||||
|
||||
logging.info(f"✓ JSON salvat: {json_path}")
|
||||
logging.info(f"Total conturi: {len(accounts)}, Total RON: {total_ron:.2f}")
|
||||
|
||||
return csv_path, json_path
|
||||
|
||||
def send_notifications(self, csv_path, downloaded_files, accounts):
|
||||
"""
|
||||
Trimite notificări email și Discord cu fișierele descărcate
|
||||
|
||||
Args:
|
||||
csv_path: Calea către fișierul CSV cu solduri
|
||||
downloaded_files: Lista de dicționare cu fișierele de tranzacții descărcate
|
||||
accounts: Lista cu datele conturilor (solduri, nume, etc.)
|
||||
"""
|
||||
try:
|
||||
from notifications import NotificationService
|
||||
|
||||
self._update_progress("Trimit rezultate...")
|
||||
logging.info("=" * 60)
|
||||
logging.info("TRIMITERE NOTIFICĂRI")
|
||||
logging.info("=" * 60)
|
||||
|
||||
# Colectează toate fișierele de trimis
|
||||
files_to_send = [str(csv_path)] # CSV cu solduri
|
||||
|
||||
# Adaugă toate CSV-urile cu tranzacții
|
||||
for df in downloaded_files:
|
||||
if 'fisier' in df and Path(df['fisier']).exists():
|
||||
files_to_send.append(df['fisier'])
|
||||
logging.info(f"Adăugat pentru notificare: {Path(df['fisier']).name}")
|
||||
|
||||
logging.info(f"Total fișiere de trimis: {len(files_to_send)}")
|
||||
|
||||
# Trimite prin toate canalele configurate
|
||||
service = NotificationService(self.config)
|
||||
logging.info(f"Calling send_all with telegram_message_id={self.telegram_message_id}, telegram_chat_id={self.telegram_chat_id}")
|
||||
results = service.send_all(
|
||||
files_to_send,
|
||||
accounts,
|
||||
telegram_message_id=self.telegram_message_id,
|
||||
telegram_chat_id=self.telegram_chat_id
|
||||
)
|
||||
|
||||
# Afișează rezumat
|
||||
if self.config.EMAIL_ENABLED:
|
||||
status = "✓ Succes" if results['email'] else "✗ Eșuat"
|
||||
logging.info(f"Email: {status}")
|
||||
|
||||
if self.config.TELEGRAM_ENABLED:
|
||||
status = "✓ Succes" if results['telegram'] else "✗ Eșuat"
|
||||
logging.info(f"Telegram: {status}")
|
||||
|
||||
logging.info("=" * 60)
|
||||
|
||||
except Exception as e:
|
||||
logging.warning(f"Notificările au eșuat: {e}")
|
||||
logging.warning("Scraper-ul a finalizat cu succes, dar notificările nu au fost trimise")
|
||||
# Nu aruncă excepția mai departe - notificările sunt opționale
|
||||
|
||||
def _handle_error(self, error):
|
||||
"""Error handling cu screenshot"""
|
||||
logging.error("=" * 60)
|
||||
logging.error(f"EROARE: {error}")
|
||||
logging.error("=" * 60, exc_info=True)
|
||||
|
||||
# Salvare screenshot la eroare
|
||||
if self.page and self.config.SCREENSHOT_ON_ERROR:
|
||||
try:
|
||||
error_path = Path(self.config.OUTPUT_DIR) / f"error_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
|
||||
self.page.screenshot(path=str(error_path))
|
||||
logging.error(f"Screenshot eroare salvat: {error_path}")
|
||||
except Exception as e:
|
||||
logging.error(f"Nu am putut salva screenshot: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Functie main pentru rulare script"""
|
||||
try:
|
||||
scraper = BTGoScraper()
|
||||
success = scraper.run()
|
||||
exit(0 if success else 1)
|
||||
except ValueError as e:
|
||||
# Eroare de configurare
|
||||
logging.error(f"Eroare configurare: {e}")
|
||||
exit(4)
|
||||
except NotImplementedError as e:
|
||||
# Selectors nu sunt completati
|
||||
print(str(e))
|
||||
exit(5)
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare neasteptata: {e}", exc_info=True)
|
||||
exit(99)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
58
config.py
Normal file
58
config.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Configurare pentru BTGO Scraper
|
||||
Citeste configuratii din variabile de mediu (.env)
|
||||
"""
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# Incarca variabilele de mediu din fisierul .env
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class Config:
|
||||
"""Clasa pentru gestionarea configuratiilor aplicatiei"""
|
||||
|
||||
# Credentials
|
||||
BTGO_USERNAME = os.getenv('BTGO_USERNAME')
|
||||
BTGO_PASSWORD = os.getenv('BTGO_PASSWORD')
|
||||
|
||||
# Browser settings
|
||||
HEADLESS = os.getenv('HEADLESS', 'false').lower() == 'true'
|
||||
|
||||
# Timeouts
|
||||
TIMEOUT_2FA_SECONDS = int(os.getenv('TIMEOUT_2FA_SECONDS', 120))
|
||||
|
||||
# Paths
|
||||
OUTPUT_DIR = os.getenv('OUTPUT_DIR', './data')
|
||||
LOG_DIR = './logs'
|
||||
|
||||
# Features
|
||||
SCREENSHOT_ON_ERROR = os.getenv('SCREENSHOT_ON_ERROR', 'true').lower() == 'true'
|
||||
DOWNLOAD_TRANSACTIONS = os.getenv('DOWNLOAD_TRANSACTIONS', 'true').lower() == 'true'
|
||||
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
|
||||
|
||||
# Notification settings
|
||||
ENABLE_NOTIFICATIONS = os.getenv('ENABLE_NOTIFICATIONS', 'false').lower() == 'true'
|
||||
|
||||
# Email settings (SMTP)
|
||||
EMAIL_ENABLED = os.getenv('EMAIL_ENABLED', 'false').lower() == 'true'
|
||||
SMTP_SERVER = os.getenv('SMTP_SERVER')
|
||||
SMTP_PORT = int(os.getenv('SMTP_PORT', 587))
|
||||
SMTP_USERNAME = os.getenv('SMTP_USERNAME')
|
||||
SMTP_PASSWORD = os.getenv('SMTP_PASSWORD')
|
||||
EMAIL_FROM = os.getenv('EMAIL_FROM')
|
||||
EMAIL_TO = os.getenv('EMAIL_TO')
|
||||
|
||||
# Telegram settings
|
||||
TELEGRAM_ENABLED = os.getenv('TELEGRAM_ENABLED', 'false').lower() == 'true'
|
||||
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
|
||||
TELEGRAM_CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
|
||||
|
||||
@classmethod
|
||||
def validate(cls):
|
||||
"""Valideaza ca toate configuratiile obligatorii sunt setate"""
|
||||
if not cls.BTGO_USERNAME or not cls.BTGO_PASSWORD:
|
||||
raise ValueError("BTGO_USERNAME si BTGO_PASSWORD trebuie setate in .env")
|
||||
|
||||
if cls.TIMEOUT_2FA_SECONDS < 30:
|
||||
raise ValueError("TIMEOUT_2FA_SECONDS trebuie sa fie minim 30 secunde")
|
||||
1
data/.gitkeep
Normal file
1
data/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# Folder pentru rezultate (CSV, JSON, screenshots)
|
||||
322
deployment/windows/INDEX.md
Normal file
322
deployment/windows/INDEX.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 📁 Deployment Scripts - Index
|
||||
|
||||
Structura completă de scripturi pentru deployment Windows Service.
|
||||
|
||||
## 📂 Structura Directoarelor
|
||||
|
||||
```
|
||||
deployment/windows/
|
||||
├── README.md # Documentație completă
|
||||
├── QUICK_START.md # Ghid rapid 5 minute
|
||||
├── INDEX.md # Acest fișier
|
||||
├── scripts/ # Scripturi PowerShell
|
||||
│ ├── menu.ps1 # ⭐ Meniu interactiv (START HERE)
|
||||
│ ├── deploy.ps1 # 🚀 Deployment complet
|
||||
│ ├── install_service.ps1 # Instalare serviciu Windows
|
||||
│ ├── uninstall_service.ps1 # Dezinstalare serviciu
|
||||
│ ├── restart_service.ps1 # Restart rapid serviciu
|
||||
│ ├── status.ps1 # Status serviciu și logs
|
||||
│ └── view_logs.ps1 # Viewer logs interactiv
|
||||
└── tools/ # Binare (auto-download)
|
||||
├── .gitignore
|
||||
└── nssm.exe # Downloaded by install_service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚦 Quick Navigation
|
||||
|
||||
| Dacă vrei să... | Rulează |
|
||||
|-----------------|---------|
|
||||
| **⭐ Meniu interactiv** | `menu.ps1` |
|
||||
| **🆕 Instalezi prima dată** | `deploy.ps1` |
|
||||
| **📊 Verifici statusul** | `status.ps1` |
|
||||
| **🔄 Restarți serviciul** | `restart_service.ps1` |
|
||||
| **📝 Vezi logs-uri** | `view_logs.ps1` |
|
||||
| **🗑️ Dezinstalezi** | `uninstall_service.ps1` |
|
||||
| **📖 Citești ghidul** | `README.md` sau `QUICK_START.md` |
|
||||
|
||||
**💡 TIP:** Folosește `menu.ps1` pentru o experiență user-friendly!
|
||||
|
||||
---
|
||||
|
||||
## 📜 Descriere Scripturi
|
||||
|
||||
### 🚀 `deploy.bat` (Main Script)
|
||||
|
||||
**Scop:** Deployment complet one-click
|
||||
|
||||
**Ce face:**
|
||||
1. Verifică Python și dependențe
|
||||
2. Configurează `.env` (dacă nu există)
|
||||
3. Instalează requirements Python
|
||||
4. Instalează browsere Playwright
|
||||
5. Creează și pornește serviciul Windows
|
||||
6. Verifică status final
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
cd deployment\windows\scripts
|
||||
deploy.bat
|
||||
```
|
||||
|
||||
**Durată:** 2-5 minute (prima dată)
|
||||
|
||||
**Note:**
|
||||
- Necesită Administrator
|
||||
- Interactiv (așteaptă configurare `.env`)
|
||||
|
||||
---
|
||||
|
||||
### ⚙️ `install_service.bat`
|
||||
|
||||
**Scop:** Instalează DOAR serviciul Windows (fără dependențe)
|
||||
|
||||
**Ce face:**
|
||||
1. Descarcă NSSM (dacă lipsește)
|
||||
2. Verifică `.env` și bot script
|
||||
3. Instalează serviciu cu NSSM
|
||||
4. Configurează logging, restart policy
|
||||
5. Pornește serviciul
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
install_service.bat
|
||||
```
|
||||
|
||||
**Use case:** După ce ai instalat manual Python + requirements
|
||||
|
||||
---
|
||||
|
||||
### 🗑️ `uninstall_service.bat`
|
||||
|
||||
**Scop:** Elimină serviciul din Windows
|
||||
|
||||
**Ce face:**
|
||||
1. Oprește serviciul (dacă rulează)
|
||||
2. Dezinstalează cu NSSM
|
||||
3. Verifică eliminare completă
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
uninstall_service.bat
|
||||
```
|
||||
|
||||
**Note:**
|
||||
- NU șterge fișiere proiect
|
||||
- NU șterge logs
|
||||
- Doar elimină serviciul din registry
|
||||
|
||||
---
|
||||
|
||||
### 🔄 `restart_service.bat`
|
||||
|
||||
**Scop:** Restart rapid serviciu (după modificări cod)
|
||||
|
||||
**Ce face:**
|
||||
1. Stop serviciu
|
||||
2. Wait 2 secunde
|
||||
3. Start serviciu
|
||||
4. Verifică status
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
restart_service.bat
|
||||
```
|
||||
|
||||
**Use case:** După `git pull` sau modificări `.env`
|
||||
|
||||
---
|
||||
|
||||
### 📊 `status.bat`
|
||||
|
||||
**Scop:** Status detaliat serviciu + logs
|
||||
|
||||
**Ce face:**
|
||||
1. Verifică dacă serviciul există
|
||||
2. Afișează status detaliat (`sc query`)
|
||||
3. Afișează configurație (`sc qc`)
|
||||
4. Afișează ultimele 10 linii din logs
|
||||
5. Afișează comenzi utile
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
status.bat
|
||||
```
|
||||
|
||||
**Use case:** Quick health check
|
||||
|
||||
---
|
||||
|
||||
### 📝 `view_logs.bat`
|
||||
|
||||
**Scop:** Viewer interactiv logs
|
||||
|
||||
**Meniu:**
|
||||
1. View stdout (toate logs)
|
||||
2. View stderr (doar erori)
|
||||
3. Tail stdout (live updates)
|
||||
4. Tail stderr (live erori)
|
||||
5. Open Explorer în folder logs
|
||||
6. Șterge toate logs
|
||||
|
||||
**Rulare:**
|
||||
```batch
|
||||
view_logs.bat
|
||||
```
|
||||
|
||||
**Use case:** Debugging, monitoring live
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configurare Serviciu Windows
|
||||
|
||||
### Detalii Serviciu
|
||||
|
||||
| Proprietate | Valoare |
|
||||
|------------|---------|
|
||||
| **Service Name** | `BTGOTelegramBot` |
|
||||
| **Display Name** | `BTGO Telegram Trigger Bot` |
|
||||
| **Executable** | `python.exe` |
|
||||
| **Arguments** | `telegram_trigger_bot.py` |
|
||||
| **Working Dir** | Project root |
|
||||
| **Start Type** | Automatic (Delayed) |
|
||||
| **Restart Policy** | On failure, 5s delay |
|
||||
| **Stdout Log** | `logs/telegram_bot_stdout.log` |
|
||||
| **Stderr Log** | `logs/telegram_bot_stderr.log` |
|
||||
| **Log Rotation** | 10 MB max |
|
||||
|
||||
### Managed by NSSM
|
||||
|
||||
**NSSM** (Non-Sucking Service Manager) este folosit pentru:
|
||||
- ✅ Simplifică crearea serviciilor din Python scripts
|
||||
- ✅ Gestionează logging automat
|
||||
- ✅ Auto-restart la crash
|
||||
- ✅ Gestiune environment variables
|
||||
|
||||
**Download:** Auto-descărcat de `install_service.bat` în `tools/nssm.exe`
|
||||
|
||||
**Versiune:** 2.24 (64-bit)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Workflow Tipic
|
||||
|
||||
### Prima Instalare (VM nou)
|
||||
|
||||
```batch
|
||||
# 1. Clone repo
|
||||
cd E:\proiecte
|
||||
git clone <repo> btgo-playwright
|
||||
cd btgo-playwright
|
||||
|
||||
# 2. Configurează .env
|
||||
copy .env.example .env
|
||||
notepad .env
|
||||
|
||||
# 3. Deploy complet
|
||||
cd deployment\windows\scripts
|
||||
deploy.bat
|
||||
|
||||
# 4. Verifică status
|
||||
status.bat
|
||||
|
||||
# 5. Test în Telegram
|
||||
# Trimite /start la bot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Update Cod (după modificări)
|
||||
|
||||
```batch
|
||||
# 1. Pull changes
|
||||
cd E:\proiecte\btgo-playwright
|
||||
git pull
|
||||
|
||||
# 2. Update requirements (dacă s-au modificat)
|
||||
python -m pip install -r requirements.txt
|
||||
|
||||
# 3. Restart serviciu
|
||||
deployment\windows\scripts\restart_service.bat
|
||||
|
||||
# 4. Verifică logs
|
||||
deployment\windows\scripts\view_logs.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Debug Issues
|
||||
|
||||
```batch
|
||||
# 1. Verifică status
|
||||
deployment\windows\scripts\status.bat
|
||||
|
||||
# 2. Verifică logs
|
||||
deployment\windows\scripts\view_logs.bat
|
||||
# Selectează opțiunea 2 (stderr)
|
||||
|
||||
# 3. Testează manual (dacă e nevoie)
|
||||
net stop BTGOTelegramBot
|
||||
python telegram_trigger_bot.py
|
||||
# Vezi output direct în terminal
|
||||
|
||||
# 4. Restart serviciu
|
||||
deployment\windows\scripts\restart_service.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Reinstalare Completă
|
||||
|
||||
```batch
|
||||
# 1. Dezinstalează
|
||||
deployment\windows\scripts\uninstall_service.bat
|
||||
|
||||
# 2. Șterge logs vechi (opțional)
|
||||
del /q logs\*.log
|
||||
|
||||
# 3. Reinstalează
|
||||
deployment\windows\scripts\deploy.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Securitate
|
||||
|
||||
**Protecție `.env`:**
|
||||
- `.env` este în `.gitignore` (nu se commitează)
|
||||
- Conține credențiale sensibile (bot token, parole BTGO)
|
||||
- Setează permissions doar pentru Administrator
|
||||
|
||||
**User IDs Telegram:**
|
||||
- `TELEGRAM_ALLOWED_USER_IDS` restricționează accesul
|
||||
- Format: `123456789,987654321` (comma-separated)
|
||||
- Nu lăsa gol (permite oricui)
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
**Documentație:**
|
||||
- `README.md` - Documentație completă
|
||||
- `QUICK_START.md` - Ghid rapid 5 minute
|
||||
- `../../TELEGRAM_BOT_SETUP.md` - Setup Telegram
|
||||
- `../../CLAUDE.md` - Documentație proiect
|
||||
|
||||
**Troubleshooting:**
|
||||
1. Verifică `status.bat`
|
||||
2. Verifică `view_logs.bat` → stderr
|
||||
3. Testează manual: `python telegram_trigger_bot.py`
|
||||
|
||||
---
|
||||
|
||||
## 📄 Changelog
|
||||
|
||||
| Data | Versiune | Modificări |
|
||||
|------|----------|-----------|
|
||||
| 2025-11-06 | 1.0 | Initial release - Complete deployment suite |
|
||||
|
||||
---
|
||||
|
||||
**Happy Deploying! 🚀**
|
||||
264
deployment/windows/QUICK_START.md
Normal file
264
deployment/windows/QUICK_START.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# 🚀 Quick Start - BTGO Telegram Bot pe VM Windows
|
||||
|
||||
Ghid rapid pentru deployment în 5 minute.
|
||||
|
||||
## ✅ Pre-requisites Checklist
|
||||
|
||||
- [ ] Windows Server/Desktop cu GUI (pentru browser vizibil)
|
||||
- [ ] Python 3.11+ instalat în PATH
|
||||
- [ ] Cont Administrator pe Windows
|
||||
- [ ] Bot Telegram creat cu @BotFather
|
||||
- [ ] Credențiale BTGO (LOGIN_ID + PAROLA)
|
||||
|
||||
---
|
||||
|
||||
## 📥 Pas 1: Clone Repository (pe VM)
|
||||
|
||||
```batch
|
||||
cd E:\proiecte
|
||||
git clone <repo-url> btgo-playwright
|
||||
cd btgo-playwright
|
||||
```
|
||||
|
||||
Sau copiază manualmente folderul pe VM.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Pas 2: Configurare `.env`
|
||||
|
||||
```batch
|
||||
REM Copiază template
|
||||
copy .env.example .env
|
||||
|
||||
REM Editează cu Notepad
|
||||
notepad .env
|
||||
```
|
||||
|
||||
**Configurare minimă necesară:**
|
||||
|
||||
```ini
|
||||
# TELEGRAM
|
||||
TELEGRAM_BOT_TOKEN=123456:ABC-DEF... # De la @BotFather
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789 # Telegram User ID-ul tău
|
||||
|
||||
# BTGO
|
||||
LOGIN_ID=your_btgo_username
|
||||
PAROLA=your_btgo_password
|
||||
|
||||
# SCRAPER
|
||||
HEADLESS=false # ⚠️ CRITICAL: NU schimba în true!
|
||||
```
|
||||
|
||||
**Cum afli User ID-ul tău:**
|
||||
|
||||
```batch
|
||||
REM După ce ai setat TELEGRAM_BOT_TOKEN în .env
|
||||
python get_telegram_chat_id.py
|
||||
REM Trimite un mesaj bot-ului în Telegram
|
||||
REM Script-ul va afișa User ID-ul
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Pas 3: Deploy Automat
|
||||
|
||||
**Right-click pe `deploy.ps1` → "Run with PowerShell" (ca Administrator)**
|
||||
|
||||
SAU din PowerShell Administrator:
|
||||
```powershell
|
||||
cd deployment\windows\scripts
|
||||
.\deploy.ps1
|
||||
```
|
||||
|
||||
**Scriptul va instala:**
|
||||
- ✅ Dependențe Python
|
||||
- ✅ Playwright browsers
|
||||
- ✅ Serviciu Windows
|
||||
- ✅ Auto-start la boot
|
||||
|
||||
**Durată:** ~2-3 minute
|
||||
|
||||
---
|
||||
|
||||
## ✅ Pas 4: Verificare
|
||||
|
||||
### 1. Verifică serviciul rulează
|
||||
|
||||
```batch
|
||||
sc query BTGOTelegramBot
|
||||
```
|
||||
|
||||
**Output așteptat:**
|
||||
```
|
||||
STATE : 4 RUNNING
|
||||
```
|
||||
|
||||
### 2. Verifică logs
|
||||
|
||||
```batch
|
||||
deployment\windows\scripts\view_logs.bat
|
||||
```
|
||||
|
||||
Sau direct:
|
||||
```batch
|
||||
type logs\telegram_bot_stdout.log
|
||||
```
|
||||
|
||||
**Ar trebui să vezi:**
|
||||
```
|
||||
[2025-11-06 10:15:23] [INFO] Bot inițializat. Useri autorizați: [123456789]
|
||||
[2025-11-06 10:15:23] [INFO] Bot pornit. Așteaptă comenzi...
|
||||
```
|
||||
|
||||
### 3. Testează în Telegram
|
||||
|
||||
1. Deschide Telegram
|
||||
2. Caută bot-ul tău (numele setat la @BotFather)
|
||||
3. Trimite `/start`
|
||||
|
||||
**Răspuns așteptat:**
|
||||
```
|
||||
🤖 BTGO Scraper Trigger Bot
|
||||
|
||||
Comenzi disponibile:
|
||||
• /scrape - Rulează scraper-ul
|
||||
• /status - Status sistem
|
||||
• /help - Ajutor
|
||||
```
|
||||
|
||||
4. Trimite `/scrape` pentru test complet
|
||||
|
||||
**Flow 2FA:**
|
||||
- Bot răspunde: "🤖 BTGO Scraper pornit... Așteaptă 2FA pe telefon!"
|
||||
- Primești notificare pe telefon în George App
|
||||
- Aprobi 2FA
|
||||
- Bot trimite fișierele (solduri + tranzacții CSV)
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Gata!
|
||||
|
||||
Serviciul rulează acum non-stop pe VM. La reboot, pornește automat.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Comenzi Utile Day-to-Day
|
||||
|
||||
### Meniu Interactiv (Cel mai ușor!)
|
||||
|
||||
```powershell
|
||||
# Right-click → "Run with PowerShell" (ca Admin)
|
||||
deployment\windows\scripts\menu.ps1
|
||||
```
|
||||
|
||||
### Comenzi Directe PowerShell
|
||||
|
||||
```powershell
|
||||
# Status rapid
|
||||
.\status.ps1
|
||||
|
||||
# Restart serviciu (după modificări cod)
|
||||
.\restart_service.ps1
|
||||
|
||||
# View logs live
|
||||
.\view_logs.ps1
|
||||
|
||||
# Oprește serviciu
|
||||
Stop-Service BTGOTelegramBot
|
||||
|
||||
# Pornește serviciu
|
||||
Start-Service BTGOTelegramBot
|
||||
|
||||
# Restart serviciu
|
||||
Restart-Service BTGOTelegramBot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Update Cod (după modificări)
|
||||
|
||||
```powershell
|
||||
# Pe VM
|
||||
cd E:\proiecte\btgo-playwright
|
||||
git pull
|
||||
|
||||
# Restart serviciu (PowerShell)
|
||||
deployment\windows\scripts\restart_service.ps1
|
||||
|
||||
# SAU folosește meniul interactiv
|
||||
deployment\windows\scripts\menu.ps1
|
||||
# → Selectează opțiunea 6 (Restart)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting Rapid
|
||||
|
||||
### Serviciul nu pornește
|
||||
|
||||
```batch
|
||||
# Verifică logs
|
||||
type logs\telegram_bot_stderr.log
|
||||
```
|
||||
|
||||
**Erori comune:**
|
||||
|
||||
**"ModuleNotFoundError":**
|
||||
```batch
|
||||
python -m pip install -r requirements.txt
|
||||
deployment\windows\scripts\restart_service.bat
|
||||
```
|
||||
|
||||
**"TELEGRAM_BOT_TOKEN nu este setat":**
|
||||
- Verifică `.env` există și conține token-ul
|
||||
- Token-ul trebuie pe o singură linie, fără spații extra
|
||||
|
||||
**"Access Denied" în browser:**
|
||||
- Verifică `HEADLESS=false` în `.env`
|
||||
- Nu folosi Docker/headless mode
|
||||
|
||||
### Bot nu răspunde în Telegram
|
||||
|
||||
1. **Verifică serviciul:**
|
||||
```batch
|
||||
sc query BTGOTelegramBot
|
||||
```
|
||||
|
||||
2. **Testează manual:**
|
||||
```batch
|
||||
REM Oprește serviciul
|
||||
net stop BTGOTelegramBot
|
||||
|
||||
REM Rulează manual pentru debug
|
||||
python telegram_trigger_bot.py
|
||||
|
||||
REM Trimite /start în Telegram
|
||||
REM Vezi output-ul direct în terminal
|
||||
```
|
||||
|
||||
3. **Verifică firewall:**
|
||||
- Bot-ul trebuie să acceseze `api.telegram.org` (port 443)
|
||||
|
||||
### Scraper-ul timeout-ează la 2FA
|
||||
|
||||
În `.env`:
|
||||
```ini
|
||||
TIMEOUT_2FA_SECONDS=600 # Crește la 10 minute
|
||||
```
|
||||
|
||||
Apoi restart:
|
||||
```batch
|
||||
deployment\windows\scripts\restart_service.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Ajutor Suplimentar
|
||||
|
||||
- **README complet:** `deployment\windows\README.md`
|
||||
- **Setup Telegram:** `TELEGRAM_BOT_SETUP.md`
|
||||
- **Documentație proiect:** `CLAUDE.md`
|
||||
|
||||
---
|
||||
|
||||
**Enjoy! 🎉**
|
||||
341
deployment/windows/README.md
Normal file
341
deployment/windows/README.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# BTGO Telegram Bot - Windows Service Deployment
|
||||
|
||||
Scripturi pentru instalarea și gestionarea bot-ului Telegram ca serviciu Windows pe VM.
|
||||
|
||||
## 📋 Cerințe Preliminare
|
||||
|
||||
- **Windows Server** 2016+ sau **Windows 10/11**
|
||||
- **Python 3.11+** instalat și adăugat în PATH
|
||||
- **Privilegii de Administrator**
|
||||
- **NSSM** (Non-Sucking Service Manager) - se descarcă automat
|
||||
- **Git** (opțional, pentru pull updates)
|
||||
|
||||
## 🚀 Deployment Complet (Recomandat)
|
||||
|
||||
```powershell
|
||||
# Right-click pe script → "Run with PowerShell" (ca Administrator)
|
||||
deployment\windows\scripts\deploy.ps1
|
||||
|
||||
# SAU din PowerShell Administrator:
|
||||
cd deployment\windows\scripts
|
||||
.\deploy.ps1
|
||||
```
|
||||
|
||||
**Acest script va:**
|
||||
1. ✅ Verifica Python și dependențe
|
||||
2. ✅ Copia și configura `.env` (dacă nu există)
|
||||
3. ✅ Instala dependențele Python (`requirements.txt`)
|
||||
4. ✅ Instala browsere Playwright
|
||||
5. ✅ Instala și porni serviciul Windows
|
||||
6. ✅ Verifica statusul serviciului
|
||||
|
||||
### ⚠️ Înainte de Prima Rulare
|
||||
|
||||
**Configurați `.env`** cu datele tale:
|
||||
|
||||
```bash
|
||||
# Telegram Bot
|
||||
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
|
||||
TELEGRAM_ALLOWED_USER_IDS=123456789,987654321 # User IDs autorizați
|
||||
TELEGRAM_CHAT_ID=123456789 # Optional: pentru notificări
|
||||
|
||||
# BTGO Credentials
|
||||
LOGIN_ID=your_btgo_login
|
||||
PAROLA=your_btgo_password
|
||||
|
||||
# Scraper Settings
|
||||
HEADLESS=false # IMPORTANT: trebuie false pentru VM Windows (WAF bypass)
|
||||
TIMEOUT_2FA_SECONDS=300
|
||||
```
|
||||
|
||||
**Cum obții variabilele Telegram:**
|
||||
|
||||
1. **Bot Token**: Vorbește cu [@BotFather](https://t.me/BotFather) și creează un bot
|
||||
2. **User IDs**: Rulează `python get_telegram_chat_id.py` după ce configurezi token-ul
|
||||
|
||||
## 📦 Scripturi Disponibile
|
||||
|
||||
Toate scripturile sunt **PowerShell** (`.ps1`) pentru Windows 10/11.
|
||||
|
||||
### `menu.ps1` - Meniu Interactiv ⭐
|
||||
Interfață user-friendly pentru toate operațiunile.
|
||||
|
||||
```powershell
|
||||
.\menu.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `deploy.ps1` - Deployment Complet
|
||||
Instalează tot ce e necesar și pornește serviciul.
|
||||
|
||||
```powershell
|
||||
.\deploy.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `install_service.ps1` - Doar Instalare Serviciu
|
||||
Instalează serviciul Windows (după ce ai configurat manual tot restul).
|
||||
|
||||
```powershell
|
||||
.\install_service.ps1
|
||||
```
|
||||
|
||||
**Ce face:**
|
||||
- Descarcă NSSM dacă nu există
|
||||
- Creează serviciul Windows `BTGOTelegramBot`
|
||||
- Configurează logging automat în `logs/`
|
||||
- Configurează auto-restart la crash
|
||||
- Pornește serviciul
|
||||
|
||||
---
|
||||
|
||||
### `uninstall_service.ps1` - Dezinstalare Serviciu
|
||||
Oprește și elimină serviciul din sistem.
|
||||
|
||||
```powershell
|
||||
.\uninstall_service.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `restart_service.ps1` - Restart Rapid
|
||||
Restart rapid al serviciului (pentru aplicarea unor modificări).
|
||||
|
||||
```powershell
|
||||
.\restart_service.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `status.ps1` - Status Serviciu
|
||||
Afișează status detaliat al serviciului și ultimele logs.
|
||||
|
||||
```powershell
|
||||
.\status.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `view_logs.ps1` - Viewer Logs Interactiv
|
||||
Meniu interactiv pentru vizualizarea logs-urilor în timp real.
|
||||
|
||||
```powershell
|
||||
.\view_logs.ps1
|
||||
```
|
||||
|
||||
**Opțiuni:**
|
||||
1. View stdout (output normal)
|
||||
2. View stderr (erori)
|
||||
3. Tail stdout (timp real)
|
||||
4. Tail stderr (timp real)
|
||||
5. Deschide Explorer în director logs
|
||||
6. Șterge toate logurile
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Gestionare Serviciu
|
||||
|
||||
### Comenzi Windows Standard
|
||||
|
||||
```batch
|
||||
# Status serviciu
|
||||
sc query BTGOTelegramBot
|
||||
|
||||
# Oprește serviciu
|
||||
net stop BTGOTelegramBot
|
||||
|
||||
# Pornește serviciu
|
||||
net start BTGOTelegramBot
|
||||
|
||||
# Restart serviciu
|
||||
net stop BTGOTelegramBot && net start BTGOTelegramBot
|
||||
|
||||
# Configurație serviciu
|
||||
sc qc BTGOTelegramBot
|
||||
|
||||
# Șterge serviciu (fără script)
|
||||
sc delete BTGOTelegramBot
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
Logs-urile se salvează automat în `logs/`:
|
||||
|
||||
- `telegram_bot_stdout.log` - Output normal (comenzi, mesaje)
|
||||
- `telegram_bot_stderr.log` - Erori și avertizări
|
||||
|
||||
**View logs în PowerShell (timp real):**
|
||||
|
||||
```powershell
|
||||
Get-Content logs\telegram_bot_stdout.log -Wait -Tail 20
|
||||
```
|
||||
|
||||
**Rotație logs:**
|
||||
- Max 10 MB per fișier
|
||||
- Rotație automată când se atinge limita
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Update și Redeploy
|
||||
|
||||
### Update Manual (cu Git)
|
||||
|
||||
```batch
|
||||
# Pe VM Windows
|
||||
cd E:\proiecte\btgo-playwright
|
||||
|
||||
# Pull ultimele modificări
|
||||
git pull origin main
|
||||
|
||||
# Redeploy serviciu
|
||||
deployment\windows\scripts\restart_service.bat
|
||||
```
|
||||
|
||||
### Redeploy Complet (Reinstalare)
|
||||
|
||||
Dacă ai modificat dependențe sau structura proiectului:
|
||||
|
||||
```batch
|
||||
# Dezinstalează
|
||||
deployment\windows\scripts\uninstall_service.bat
|
||||
|
||||
# Reinstalează tot
|
||||
deployment\windows\scripts\deploy.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Serviciul nu pornește
|
||||
|
||||
**Verifică logs:**
|
||||
```batch
|
||||
deployment\windows\scripts\view_logs.bat
|
||||
```
|
||||
|
||||
**Probleme comune:**
|
||||
|
||||
1. **Python nu este în PATH**
|
||||
- Reinstalează Python cu "Add to PATH" bifat
|
||||
- Sau setează manual PATH în Environment Variables
|
||||
|
||||
2. **`.env` lipsă sau invalid**
|
||||
- Verifică că `.env` există în root-ul proiectului
|
||||
- Verifică că `TELEGRAM_BOT_TOKEN` este setat corect
|
||||
|
||||
3. **Playwright browser lipsă**
|
||||
```batch
|
||||
python -m playwright install chromium
|
||||
```
|
||||
|
||||
4. **Port ocupat / Serviciu duplicat**
|
||||
```batch
|
||||
uninstall_service.bat
|
||||
# Așteaptă 10 secunde
|
||||
install_service.bat
|
||||
```
|
||||
|
||||
### Bot nu răspunde în Telegram
|
||||
|
||||
1. **Verifică serviciul rulează:**
|
||||
```batch
|
||||
sc query BTGOTelegramBot
|
||||
```
|
||||
|
||||
2. **Verifică logs pentru erori:**
|
||||
```batch
|
||||
type logs\telegram_bot_stderr.log
|
||||
```
|
||||
|
||||
3. **Testează bot-ul manual:**
|
||||
```batch
|
||||
python telegram_trigger_bot.py
|
||||
# Trimite /start în Telegram
|
||||
# Ctrl+C pentru a opri
|
||||
```
|
||||
|
||||
4. **Verifică firewall:**
|
||||
- Bot-ul trebuie să poată accesa `api.telegram.org` (HTTPS/443)
|
||||
|
||||
### Scraper-ul nu funcționează (Access Denied)
|
||||
|
||||
**CRITICAL:** Docker/headless mode NU funcționează din cauza Akamai WAF.
|
||||
|
||||
**Soluție:**
|
||||
- Setează `HEADLESS=false` în `.env`
|
||||
- Bot-ul TREBUIE să ruleze pe Windows cu browser vizibil
|
||||
- Asigură-te că VM-ul are desktop environment activ
|
||||
|
||||
### Performance Issues
|
||||
|
||||
**Bot lent / Scraper timeout:**
|
||||
- Crește `TIMEOUT_2FA_SECONDS` în `.env`
|
||||
- Verifică resursele VM-ului (CPU, RAM)
|
||||
- Verifică conexiunea la internet a VM-ului
|
||||
|
||||
---
|
||||
|
||||
## 📊 Arhitectura Serviciului
|
||||
|
||||
```
|
||||
BTGOTelegramBot (Windows Service)
|
||||
├── Managed by: NSSM
|
||||
├── Auto-start: Yes (Delayed)
|
||||
├── Restart on crash: Yes (5s delay)
|
||||
├── Working dir: E:\proiecte\btgo-playwright
|
||||
├── Python script: telegram_trigger_bot.py
|
||||
├── Logs: logs\telegram_bot_*.log
|
||||
└── Config: .env
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
User /scrape → Telegram API → Bot Service → btgo_scraper.py
|
||||
├── Playwright login
|
||||
├── 2FA wait
|
||||
├── Extract accounts
|
||||
├── Download CSVs
|
||||
└── Send files via notifiers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Securitate
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Restricționează accesul bot-ului:**
|
||||
- Setează `TELEGRAM_ALLOWED_USER_IDS` în `.env`
|
||||
- Nu lăsa lista goală (permite oricui)
|
||||
|
||||
2. **Protejează `.env`:**
|
||||
- Nu comite `.env` în Git (este în `.gitignore`)
|
||||
- Setează permisiuni restrictive pe VM
|
||||
|
||||
3. **Monitorizează logs-urile:**
|
||||
- Verifică periodic `stderr.log` pentru erori
|
||||
- Setup alerting pentru crash-uri
|
||||
|
||||
4. **Rotație credentials:**
|
||||
- Regenerează `TELEGRAM_BOT_TOKEN` periodic
|
||||
- Update `LOGIN_ID`/`PAROLA` la schimbări
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
**Probleme sau întrebări?**
|
||||
|
||||
1. Verifică logs: `deployment\windows\scripts\view_logs.bat`
|
||||
2. Verifică status: `deployment\windows\scripts\status.bat`
|
||||
3. Consultă documentația principală: `TELEGRAM_BOT_SETUP.md`
|
||||
4. Check issue tracker: [GitHub Issues](../../../)
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licență
|
||||
|
||||
Acest tool este pentru **uz personal și educațional**. Asigură-te că respecti termenii și condițiile băncii tale.
|
||||
282
deployment/windows/scripts/deploy.ps1
Normal file
282
deployment/windows/scripts/deploy.ps1
Normal file
@@ -0,0 +1,282 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Complete Deployment Script
|
||||
.DESCRIPTION
|
||||
Deployment complet pentru bot-ul Telegram pe Windows VM
|
||||
Verifica dependente, instaleaza requirements, configureaza serviciul
|
||||
.NOTES
|
||||
Rulare: Right-click → "Run with PowerShell" (ca Administrator)
|
||||
#>
|
||||
|
||||
# Error handling
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# ============================================================================
|
||||
# FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
function Write-ColorOutput {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Color = "White",
|
||||
[string]$Prefix = ""
|
||||
)
|
||||
|
||||
$prefixColor = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
"[AVERTIZARE]" { "Yellow" }
|
||||
default { "White" }
|
||||
}
|
||||
|
||||
if ($Prefix) {
|
||||
Write-Host $Prefix -ForegroundColor $prefixColor -NoNewline
|
||||
Write-Host " $Message" -ForegroundColor $Color
|
||||
} else {
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Separator {
|
||||
param([string]$Title = "")
|
||||
Write-Host ""
|
||||
Write-Host "=" * 80 -ForegroundColor Cyan
|
||||
if ($Title) {
|
||||
Write-Host $Title -ForegroundColor Yellow
|
||||
Write-Host "=" * 80 -ForegroundColor Cyan
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
|
||||
Write-Separator "BTGO TELEGRAM BOT - DEPLOYMENT COMPLET"
|
||||
Write-Host ""
|
||||
Write-ColorOutput "Director proiect: $ProjectDir" -Prefix "[INFO]"
|
||||
Write-Host ""
|
||||
|
||||
# ============================================================================
|
||||
# [1/6] VERIFICARE PYTHON
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[1/6] VERIFICARE PYTHON"
|
||||
|
||||
try {
|
||||
$pythonVersion = python --version 2>&1
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
Write-ColorOutput "Python gasit: $pythonVersion" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Python nu este instalat sau nu este in PATH!" -Prefix "[EROARE]"
|
||||
Write-Host ""
|
||||
Write-Host "Instaleaza Python 3.11+ de la: https://www.python.org/downloads/"
|
||||
Write-Host "Asigura-te ca bifezi 'Add Python to PATH' la instalare"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# [2/6] VERIFICARE FISIERE PROIECT
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[2/6] VERIFICARE FISIERE PROIECT"
|
||||
|
||||
$criticalFiles = @(
|
||||
"telegram_trigger_bot.py",
|
||||
"btgo_scraper.py",
|
||||
"requirements.txt"
|
||||
)
|
||||
|
||||
$filesOk = $true
|
||||
foreach ($file in $criticalFiles) {
|
||||
$filePath = Join-Path $ProjectDir $file
|
||||
if (Test-Path $filePath) {
|
||||
Write-ColorOutput "$file - OK" -Prefix "[OK]"
|
||||
} else {
|
||||
Write-ColorOutput "$file - LIPSESTE!" -Prefix "[EROARE]"
|
||||
$filesOk = $false
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $filesOk) {
|
||||
Write-ColorOutput "Fisiere critice lipsesc din proiect!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# [3/6] VERIFICARE/CREARE .env
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[3/6] VERIFICARE CONFIGURARE (.env)"
|
||||
|
||||
$envFile = Join-Path $ProjectDir ".env"
|
||||
$envExample = Join-Path $ProjectDir ".env.example"
|
||||
|
||||
if (-not (Test-Path $envFile)) {
|
||||
if (Test-Path $envExample) {
|
||||
Write-ColorOutput ".env nu exista. Copiem .env.example..." -Prefix "[INFO]"
|
||||
Copy-Item $envExample $envFile
|
||||
Write-ColorOutput ".env creat din .env.example" -Prefix "[OK]"
|
||||
Write-Host ""
|
||||
Write-ColorOutput "IMPORTANT: Editeaza .env si configureaza:" -Prefix "[AVERTIZARE]"
|
||||
Write-Host " - TELEGRAM_BOT_TOKEN"
|
||||
Write-Host " - TELEGRAM_ALLOWED_USER_IDS"
|
||||
Write-Host " - TELEGRAM_CHAT_ID (optional)"
|
||||
Write-Host " - LOGIN_ID si PAROLA pentru BTGO"
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter dupa ce ai configurat .env"
|
||||
} else {
|
||||
Write-ColorOutput ".env si .env.example lipsesc!" -Prefix "[EROARE]"
|
||||
Write-Host "Creaza manual .env cu variabilele necesare"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Verifică variabile critice
|
||||
$envContent = Get-Content $envFile -Raw
|
||||
if ($envContent -notmatch "TELEGRAM_BOT_TOKEN=(?!your_).+") {
|
||||
Write-ColorOutput "TELEGRAM_BOT_TOKEN nu este configurat in .env!" -Prefix "[EROARE]"
|
||||
Write-Host "Editeaza .env si adauga token-ul de la @BotFather"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($envContent -notmatch "LOGIN_ID=(?!your_).+") {
|
||||
Write-ColorOutput "LOGIN_ID nu este configurat in .env!" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Scraper-ul nu va functiona fara credentiale BTGO"
|
||||
}
|
||||
|
||||
Write-ColorOutput ".env exista si pare configurat" -Prefix "[OK]"
|
||||
|
||||
# ============================================================================
|
||||
# [4/6] INSTALARE DEPENDENTE PYTHON
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[4/6] INSTALARE DEPENDENTE PYTHON"
|
||||
|
||||
try {
|
||||
Write-ColorOutput "Upgrade pip..." -Prefix "[INFO]"
|
||||
python -m pip install --upgrade pip --quiet
|
||||
|
||||
Write-ColorOutput "Instalare requirements.txt..." -Prefix "[INFO]"
|
||||
$requirementsFile = Join-Path $ProjectDir "requirements.txt"
|
||||
python -m pip install -r $requirementsFile
|
||||
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
Write-ColorOutput "Dependente instalate cu succes" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Instalarea dependentelor a esuat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# [5/6] INSTALARE PLAYWRIGHT BROWSERS (GLOBAL)
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[5/6] INSTALARE PLAYWRIGHT BROWSERS (GLOBAL)"
|
||||
|
||||
# Setează locație globală pentru browsere (accesibilă pentru serviciul SYSTEM)
|
||||
$globalBrowserPath = "C:\playwright-browsers"
|
||||
|
||||
Write-ColorOutput "Configurare locatie globala browsere: $globalBrowserPath" -Prefix "[INFO]"
|
||||
|
||||
# Creează directorul dacă nu există
|
||||
if (-not (Test-Path $globalBrowserPath)) {
|
||||
New-Item -ItemType Directory -Path $globalBrowserPath -Force | Out-Null
|
||||
Write-ColorOutput "Director creat: $globalBrowserPath" -Prefix "[OK]"
|
||||
}
|
||||
|
||||
# Acordă permisiuni SYSTEM la director
|
||||
Write-ColorOutput "Acordare permisiuni SYSTEM..." -Prefix "[INFO]"
|
||||
icacls $globalBrowserPath /grant "SYSTEM:(OI)(CI)F" /T 2>&1 | Out-Null
|
||||
|
||||
# Setează environment variable la nivel de sistem
|
||||
[System.Environment]::SetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH", $globalBrowserPath, [System.EnvironmentVariableTarget]::Machine)
|
||||
$env:PLAYWRIGHT_BROWSERS_PATH = $globalBrowserPath
|
||||
Write-ColorOutput "Environment variable setata: PLAYWRIGHT_BROWSERS_PATH" -Prefix "[OK]"
|
||||
|
||||
# Instalează browserele
|
||||
try {
|
||||
Write-ColorOutput "Instalare browsere Playwright in $globalBrowserPath..." -Prefix "[INFO]"
|
||||
Write-Host "Aceasta poate dura 1-2 minute..." -ForegroundColor Gray
|
||||
python -m playwright install chromium
|
||||
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
Write-ColorOutput "Playwright browsere instalate cu succes!" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Instalarea browserelor Playwright a esuat!" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Scraper-ul poate sa nu functioneze corect"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# CREARE DIRECTOARE NECESARE
|
||||
# ============================================================================
|
||||
|
||||
$dataDir = Join-Path $ProjectDir "data"
|
||||
$logsDir = Join-Path $ProjectDir "logs"
|
||||
|
||||
if (-not (Test-Path $dataDir)) { New-Item -ItemType Directory -Path $dataDir -Force | Out-Null }
|
||||
if (-not (Test-Path $logsDir)) { New-Item -ItemType Directory -Path $logsDir -Force | Out-Null }
|
||||
|
||||
Write-ColorOutput "Directoare create: data, logs" -Prefix "[OK]"
|
||||
|
||||
# ============================================================================
|
||||
# [6/6] INSTALARE/RESTART SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[6/6] INSTALARE SERVICIU WINDOWS"
|
||||
|
||||
# Verifică dacă serviciul există
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if ($service) {
|
||||
Write-ColorOutput "Serviciul exista deja. Reinstalam..." -Prefix "[INFO]"
|
||||
& "$ScriptDir\uninstall_service.ps1"
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
Write-ColorOutput "Rulare install_service.ps1..." -Prefix "[INFO]"
|
||||
& "$ScriptDir\install_service.ps1"
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-ColorOutput "Instalarea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# FINALIZARE
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "[SUCCES] DEPLOYMENT FINALIZAT!"
|
||||
Write-Host ""
|
||||
Write-Host "Serviciul BTGO Telegram Bot este acum instalat si ruleaza." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Comenzi utile:" -ForegroundColor Yellow
|
||||
Write-Host " - Status: Get-Service $ServiceName"
|
||||
Write-Host " - Logs: Get-Content '$logsDir\telegram_bot_stdout.log' -Tail 20"
|
||||
Write-Host " - Opreste: Stop-Service $ServiceName"
|
||||
Write-Host " - Porneste: Start-Service $ServiceName"
|
||||
Write-Host " - Restart: Restart-Service $ServiceName"
|
||||
Write-Host " - Dezinstala: .\uninstall_service.ps1"
|
||||
Write-Host ""
|
||||
Write-Host "Teste bot Telegram:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Deschide Telegram si cauta bot-ul tau"
|
||||
Write-Host " 2. Trimite /start pentru a vedea comenzile"
|
||||
Write-Host " 3. Trimite /scrape pentru a testa scraper-ul"
|
||||
Write-Host ""
|
||||
Write-Host "Verificare logs in timp real:" -ForegroundColor Yellow
|
||||
Write-Host " Get-Content '$logsDir\telegram_bot_stdout.log' -Wait"
|
||||
Write-Host ""
|
||||
Write-Separator
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
275
deployment/windows/scripts/install_service.ps1
Normal file
275
deployment/windows/scripts/install_service.ps1
Normal file
@@ -0,0 +1,275 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Service Installation Script
|
||||
.DESCRIPTION
|
||||
Instalează bot-ul Telegram ca serviciu Windows folosind NSSM
|
||||
.NOTES
|
||||
Rulare: Right-click → "Run with PowerShell" (ca Administrator)
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# ============================================================================
|
||||
# FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
function Write-ColorOutput {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Prefix = ""
|
||||
)
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
"[AVERTIZARE]" { "Yellow" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Separator {
|
||||
param([string]$Title = "")
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
if ($Title) {
|
||||
Write-Host $Title -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
$ServiceDisplayName = "BTGO Telegram Trigger Bot"
|
||||
$ServiceDescription = "Bot Telegram pentru declansarea BTGO Scraper prin comanda /scrape"
|
||||
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$BotScript = Join-Path $ProjectDir "telegram_trigger_bot.py"
|
||||
$EnvFile = Join-Path $ProjectDir ".env"
|
||||
$LogDir = Join-Path $ProjectDir "logs"
|
||||
$ToolsDir = Join-Path $ProjectDir "deployment\windows\tools"
|
||||
$NssmPath = Join-Path $ToolsDir "nssm.exe"
|
||||
|
||||
Write-Separator "BTGO TELEGRAM BOT - INSTALARE SERVICIU"
|
||||
Write-ColorOutput "Director proiect: $ProjectDir" -Prefix "[INFO]"
|
||||
|
||||
# ============================================================================
|
||||
# VERIFICARI PRE-INSTALARE
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "VERIFICARI PRE-INSTALARE"
|
||||
|
||||
# Verifică Python
|
||||
try {
|
||||
$pythonPath = (Get-Command python).Source
|
||||
Write-ColorOutput "Python: $pythonPath" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Python nu a fost gasit in PATH!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică script bot
|
||||
if (Test-Path $BotScript) {
|
||||
Write-ColorOutput "Script bot: $BotScript" -Prefix "[OK]"
|
||||
} else {
|
||||
Write-ColorOutput "Script bot nu a fost gasit: $BotScript" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică .env
|
||||
if (Test-Path $EnvFile) {
|
||||
Write-ColorOutput "Fisier .env: $EnvFile" -Prefix "[OK]"
|
||||
} else {
|
||||
Write-ColorOutput "Fisier .env nu a fost gasit: $EnvFile" -Prefix "[EROARE]"
|
||||
Write-Host "Copiaza .env.example la .env si configureaza-l"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică variabile critice din .env
|
||||
$envContent = Get-Content $EnvFile -Raw
|
||||
if ($envContent -match "TELEGRAM_BOT_TOKEN=.+") {
|
||||
Write-ColorOutput "TELEGRAM_BOT_TOKEN configurat in .env" -Prefix "[OK]"
|
||||
} else {
|
||||
Write-ColorOutput "TELEGRAM_BOT_TOKEN nu este setat in .env!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Creează director logs
|
||||
if (-not (Test-Path $LogDir)) {
|
||||
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
|
||||
Write-ColorOutput "Director logs creat: $LogDir" -Prefix "[INFO]"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# DOWNLOAD NSSM (daca nu exista)
|
||||
# ============================================================================
|
||||
|
||||
if (-not (Test-Path $NssmPath)) {
|
||||
Write-Separator "DOWNLOAD NSSM"
|
||||
Write-ColorOutput "NSSM nu a fost gasit. Descarcam..." -Prefix "[INFO]"
|
||||
|
||||
if (-not (Test-Path $ToolsDir)) {
|
||||
New-Item -ItemType Directory -Path $ToolsDir -Force | Out-Null
|
||||
}
|
||||
|
||||
try {
|
||||
$nssmUrl = "https://nssm.cc/release/nssm-2.24.zip"
|
||||
$nssmZip = Join-Path $env:TEMP "nssm.zip"
|
||||
$nssmExtract = Join-Path $env:TEMP "nssm"
|
||||
|
||||
Write-ColorOutput "Descarcare NSSM..." -Prefix "[INFO]"
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
Invoke-WebRequest -Uri $nssmUrl -OutFile $nssmZip -UseBasicParsing
|
||||
|
||||
Write-ColorOutput "Extragere NSSM..." -Prefix "[INFO]"
|
||||
Expand-Archive -Path $nssmZip -DestinationPath $nssmExtract -Force
|
||||
|
||||
# Copiază executabilul
|
||||
$nssmExe = Join-Path $nssmExtract "nssm-2.24\win64\nssm.exe"
|
||||
Copy-Item $nssmExe $NssmPath
|
||||
|
||||
# Curăță
|
||||
Remove-Item $nssmZip -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $nssmExtract -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-ColorOutput "NSSM descarcat: $NssmPath" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Descarcarea NSSM a esuat!" -Prefix "[EROARE]"
|
||||
Write-Host "Descarca manual de la: https://nssm.cc/download"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# INSTALARE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "INSTALARE SERVICIU WINDOWS"
|
||||
|
||||
# Verifică dacă serviciul există deja
|
||||
$existingService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
if ($existingService) {
|
||||
Write-ColorOutput "Serviciul $ServiceName exista deja!" -Prefix "[AVERTIZARE]"
|
||||
Write-ColorOutput "Ruland dezinstalare mai intai..." -Prefix "[INFO]"
|
||||
& "$ScriptDir\uninstall_service.ps1"
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
Write-ColorOutput "Instalare serviciu: $ServiceName" -Prefix "[INFO]"
|
||||
|
||||
# Instalează serviciul
|
||||
& $NssmPath install $ServiceName $pythonPath $BotScript
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-ColorOutput "Instalarea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURARE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-ColorOutput "Configurare serviciu..." -Prefix "[INFO]"
|
||||
|
||||
# Display name si descriere
|
||||
& $NssmPath set $ServiceName DisplayName $ServiceDisplayName
|
||||
& $NssmPath set $ServiceName Description $ServiceDescription
|
||||
|
||||
# Verifică și configurează Playwright browsers global (dacă nu e deja setat)
|
||||
$globalBrowserPath = [System.Environment]::GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH", [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
if (-not $globalBrowserPath) {
|
||||
Write-ColorOutput "PLAYWRIGHT_BROWSERS_PATH nu este setat!" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Ruleaza 'deploy.ps1' pentru instalare completa cu browsere globale"
|
||||
} else {
|
||||
Write-ColorOutput "Playwright browsere: $globalBrowserPath" -Prefix "[OK]"
|
||||
}
|
||||
|
||||
# Working directory
|
||||
& $NssmPath set $ServiceName AppDirectory $ProjectDir
|
||||
|
||||
# Stdout/Stderr logging
|
||||
$stdoutLog = Join-Path $LogDir "telegram_bot_stdout.log"
|
||||
$stderrLog = Join-Path $LogDir "telegram_bot_stderr.log"
|
||||
& $NssmPath set $ServiceName AppStdout $stdoutLog
|
||||
& $NssmPath set $ServiceName AppStderr $stderrLog
|
||||
|
||||
# Rotatie logs (10 MB max)
|
||||
& $NssmPath set $ServiceName AppStdoutCreationDisposition 4
|
||||
& $NssmPath set $ServiceName AppStderrCreationDisposition 4
|
||||
& $NssmPath set $ServiceName AppRotateFiles 1
|
||||
& $NssmPath set $ServiceName AppRotateOnline 1
|
||||
& $NssmPath set $ServiceName AppRotateBytes 10485760
|
||||
|
||||
# Restart policy (restart la crash)
|
||||
& $NssmPath set $ServiceName AppExit Default Restart
|
||||
& $NssmPath set $ServiceName AppRestartDelay 5000
|
||||
|
||||
# Start mode (Automatic Delayed Start)
|
||||
& $NssmPath set $ServiceName Start SERVICE_DELAYED_AUTO_START
|
||||
|
||||
Write-ColorOutput "Serviciu configurat cu succes!" -Prefix "[OK]"
|
||||
|
||||
# ============================================================================
|
||||
# PORNIRE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "PORNIRE SERVICIU"
|
||||
|
||||
Write-ColorOutput "Pornire serviciu: $ServiceName" -Prefix "[INFO]"
|
||||
Start-Service -Name $ServiceName
|
||||
|
||||
if ((Get-Service -Name $ServiceName).Status -ne "Running") {
|
||||
Write-ColorOutput "Pornirea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Write-Host "Verifica logurile in: $LogDir"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# ============================================================================
|
||||
# VERIFICARE STATUS
|
||||
# ============================================================================
|
||||
|
||||
$service = Get-Service -Name $ServiceName
|
||||
|
||||
if ($service.Status -eq "Running") {
|
||||
Write-Separator "[SUCCES] SERVICIU INSTALAT SI PORNIT!"
|
||||
Write-Host ""
|
||||
Write-Host "Serviciu: $ServiceName" -ForegroundColor Green
|
||||
Write-Host "Display: $ServiceDisplayName" -ForegroundColor Green
|
||||
Write-Host "Script: $BotScript" -ForegroundColor Green
|
||||
Write-Host "Logs: $LogDir" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Comenzi utile:" -ForegroundColor Yellow
|
||||
Write-Host " - Opreste: Stop-Service $ServiceName"
|
||||
Write-Host " - Porneste: Start-Service $ServiceName"
|
||||
Write-Host " - Restart: Restart-Service $ServiceName"
|
||||
Write-Host " - Status: Get-Service $ServiceName"
|
||||
Write-Host " - Dezinstaleaza: .\uninstall_service.ps1"
|
||||
Write-Host ""
|
||||
Write-Separator
|
||||
} else {
|
||||
Write-ColorOutput "Serviciul este instalat dar nu ruleaza!" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Verifica logurile: $LogDir"
|
||||
Get-Service -Name $ServiceName | Format-List
|
||||
}
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
314
deployment/windows/scripts/menu.ps1
Normal file
314
deployment/windows/scripts/menu.ps1
Normal file
@@ -0,0 +1,314 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Main Menu Launcher
|
||||
.DESCRIPTION
|
||||
Meniu interactiv pentru toate scripturile de management
|
||||
.NOTES
|
||||
Rulare: Right-click → "Run with PowerShell" (ca Administrator)
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$LogDir = Join-Path $ProjectDir "logs"
|
||||
|
||||
function Show-MainMenu {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " BTGO TELEGRAM BOT - MANAGEMENT MENU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Verifică status serviciu
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
if ($service) {
|
||||
if ($service.Status -eq "Running") {
|
||||
Write-Host "Status: " -NoNewline
|
||||
Write-Host "[RUNNING]" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "Status: " -NoNewline
|
||||
Write-Host "[STOPPED]" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "Status: " -NoNewline
|
||||
Write-Host "[NOT INSTALLED]" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
Write-Host " [1] Deploy Complet (First Time Setup)" -ForegroundColor Cyan
|
||||
Write-Host " [2] Install Service Only" -ForegroundColor Cyan
|
||||
Write-Host " [3] Uninstall Service" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host " [4] Start Service" -ForegroundColor Green
|
||||
Write-Host " [5] Stop Service" -ForegroundColor Yellow
|
||||
Write-Host " [6] Restart Service" -ForegroundColor Magenta
|
||||
Write-Host " [7] Service Status" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host " [8] View Logs (Interactive)" -ForegroundColor White
|
||||
Write-Host " [9] Quick Log Tail (Stdout)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host " Development & Manual Testing:" -ForegroundColor DarkCyan
|
||||
Write-Host " [S] Setup Development Environment" -ForegroundColor Cyan
|
||||
Write-Host " [R] Run Scraper (Manual)" -ForegroundColor Cyan
|
||||
Write-Host " [T] Run Telegram Bot (Manual)" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host " [A] Open Deployment README" -ForegroundColor Gray
|
||||
Write-Host " [B] Open Quick Start Guide" -ForegroundColor Gray
|
||||
Write-Host " [C] Open Project in Explorer" -ForegroundColor Gray
|
||||
Write-Host " [D] Open Logs in Explorer" -ForegroundColor Gray
|
||||
Write-Host " [U] Update Browsers (Playwright)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " [0] Exit" -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
function Invoke-Deploy {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "RULARE DEPLOY COMPLET" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\deploy.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Invoke-Install {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "INSTALARE SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\install_service.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Invoke-Uninstall {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "DEZINSTALARE SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\uninstall_service.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Invoke-Start {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "START SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
if (-not $service) {
|
||||
Write-Host "[EROARE] Serviciul $ServiceName nu este instalat!" -ForegroundColor Red
|
||||
Start-Sleep -Seconds 2
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "[INFO] Pornire serviciu..." -ForegroundColor Cyan
|
||||
try {
|
||||
Start-Service -Name $ServiceName
|
||||
Write-Host ""
|
||||
Write-Host "[SUCCES] Serviciu pornit!" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "[EROARE] Pornirea a esuat: $_" -ForegroundColor Red
|
||||
}
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
function Invoke-Stop {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STOP SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
if (-not $service) {
|
||||
Write-Host "[EROARE] Serviciul $ServiceName nu este instalat!" -ForegroundColor Red
|
||||
Start-Sleep -Seconds 2
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "[INFO] Oprire serviciu..." -ForegroundColor Cyan
|
||||
try {
|
||||
Stop-Service -Name $ServiceName -Force
|
||||
Write-Host ""
|
||||
Write-Host "[SUCCES] Serviciu oprit!" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "[INFO] Serviciul era deja oprit" -ForegroundColor Yellow
|
||||
}
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
function Invoke-Restart {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "RESTART SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\restart_service.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Invoke-Status {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STATUS SERVICIU" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\status.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Invoke-ViewLogs {
|
||||
Clear-Host
|
||||
& "$ScriptDir\view_logs.ps1"
|
||||
}
|
||||
|
||||
function Invoke-TailLogs {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "LOG TAIL - STDOUT (LIVE)" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "Apasa Ctrl+C pentru a opri" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
$stdoutLog = Join-Path $LogDir "telegram_bot_stdout.log"
|
||||
if (Test-Path $stdoutLog) {
|
||||
Get-Content $stdoutLog -Wait -Tail 30
|
||||
} else {
|
||||
Write-Host "[INFO] Log-ul nu exista inca" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
function Open-ReadmeFile {
|
||||
$readme = Join-Path $ProjectDir "deployment\windows\README.md"
|
||||
if (Test-Path $readme) {
|
||||
Start-Process $readme
|
||||
}
|
||||
}
|
||||
|
||||
function Open-QuickStartFile {
|
||||
$quickstart = Join-Path $ProjectDir "deployment\windows\QUICK_START.md"
|
||||
if (Test-Path $quickstart) {
|
||||
Start-Process $quickstart
|
||||
}
|
||||
}
|
||||
|
||||
function Open-ProjectExplorer {
|
||||
Start-Process explorer $ProjectDir
|
||||
}
|
||||
|
||||
function Open-LogsExplorer {
|
||||
if (Test-Path $LogDir) {
|
||||
Start-Process explorer $LogDir
|
||||
} else {
|
||||
Write-Host "[INFO] Directorul logs nu exista inca" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-SetupDev {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "SETUP DEVELOPMENT ENVIRONMENT" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\setup_dev.ps1"
|
||||
}
|
||||
|
||||
function Invoke-RunScraper {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "RUN SCRAPER (MANUAL MODE)" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\run_scraper.ps1"
|
||||
}
|
||||
|
||||
function Invoke-RunTelegramBotManual {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "RUN TELEGRAM BOT (MANUAL MODE)" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\run_telegram_bot_manual.ps1"
|
||||
}
|
||||
|
||||
function Invoke-UpdateBrowsers {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "UPDATE PLAYWRIGHT BROWSERS" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
& "$ScriptDir\update_browsers.ps1"
|
||||
Read-Host "`nApasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
# Main loop
|
||||
do {
|
||||
Show-MainMenu
|
||||
$choice = Read-Host "Selecteaza optiune"
|
||||
|
||||
switch ($choice) {
|
||||
"1" { Invoke-Deploy }
|
||||
"2" { Invoke-Install }
|
||||
"3" { Invoke-Uninstall }
|
||||
"4" { Invoke-Start }
|
||||
"5" { Invoke-Stop }
|
||||
"6" { Invoke-Restart }
|
||||
"7" { Invoke-Status }
|
||||
"8" { Invoke-ViewLogs }
|
||||
"9" { Invoke-TailLogs }
|
||||
"A" { Open-ReadmeFile }
|
||||
"a" { Open-ReadmeFile }
|
||||
"B" { Open-QuickStartFile }
|
||||
"b" { Open-QuickStartFile }
|
||||
"C" { Open-ProjectExplorer }
|
||||
"c" { Open-ProjectExplorer }
|
||||
"D" { Open-LogsExplorer }
|
||||
"d" { Open-LogsExplorer }
|
||||
"S" { Invoke-SetupDev }
|
||||
"s" { Invoke-SetupDev }
|
||||
"R" { Invoke-RunScraper }
|
||||
"r" { Invoke-RunScraper }
|
||||
"T" { Invoke-RunTelegramBotManual }
|
||||
"t" { Invoke-RunTelegramBotManual }
|
||||
"U" { Invoke-UpdateBrowsers }
|
||||
"u" { Invoke-UpdateBrowsers }
|
||||
"0" {
|
||||
Write-Host ""
|
||||
Write-Host "Goodbye!" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
default {
|
||||
Write-Host "[EROARE] Optiune invalida!" -ForegroundColor Red
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
}
|
||||
} while ($true)
|
||||
87
deployment/windows/scripts/restart_service.ps1
Normal file
87
deployment/windows/scripts/restart_service.ps1
Normal file
@@ -0,0 +1,87 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Service Restart Script
|
||||
.DESCRIPTION
|
||||
Restart rapid al serviciului
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
|
||||
function Write-ColorOutput {
|
||||
param([string]$Message, [string]$Prefix = "")
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Separator {
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "RESTART SERVICIU: $ServiceName" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Separator
|
||||
|
||||
# Verifică dacă serviciul există
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $service) {
|
||||
Write-ColorOutput "Serviciul $ServiceName nu este instalat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Oprește serviciul
|
||||
Write-ColorOutput "Oprire serviciu..." -Prefix "[INFO]"
|
||||
try {
|
||||
Stop-Service -Name $ServiceName -Force
|
||||
Write-ColorOutput "Serviciu oprit" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Oprirea a esuat. Incercam oprire fortata..." -Prefix "[AVERTIZARE]"
|
||||
sc.exe stop $ServiceName | Out-Null
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Pornește serviciul
|
||||
Write-ColorOutput "Pornire serviciu..." -Prefix "[INFO]"
|
||||
try {
|
||||
Start-Service -Name $ServiceName
|
||||
Write-ColorOutput "Serviciu pornit" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Pornirea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Write-Host "Verificati logurile pentru detalii"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Verifică status
|
||||
$service = Get-Service -Name $ServiceName
|
||||
|
||||
if ($service.Status -eq "Running") {
|
||||
Write-Host ""
|
||||
Write-ColorOutput "[SUCCES] Serviciu restartat cu succes!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Get-Service -Name $ServiceName | Format-Table -AutoSize
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-ColorOutput "Serviciul nu ruleaza dupa restart!" -Prefix "[EROARE]"
|
||||
Get-Service -Name $ServiceName | Format-Table -AutoSize
|
||||
}
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
111
deployment/windows/scripts/run_scraper.ps1
Normal file
111
deployment/windows/scripts/run_scraper.ps1
Normal file
@@ -0,0 +1,111 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Scraper - Rulare Development
|
||||
.DESCRIPTION
|
||||
Ruleaza scraper-ul BTGO in modul development (cu browser vizibil)
|
||||
.NOTES
|
||||
Echivalent PowerShell pentru scripts\run_dev.bat
|
||||
Rulare: Right-click → "Run with PowerShell"
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
|
||||
Set-Location $ProjectDir
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " BTGO SCRAPER - RULARE DEVELOPMENT" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Working directory: $ProjectDir" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Verifică dacă venv există
|
||||
$venvPath = Join-Path $ProjectDir ".venv"
|
||||
$activateScript = Join-Path $venvPath "Scripts\Activate.ps1"
|
||||
|
||||
if (-not (Test-Path $activateScript)) {
|
||||
Write-Host "[ERROR] Virtual environment nu exista sau este incomplet!" -ForegroundColor Red
|
||||
Write-Host "Ruleaza intai: setup_dev.ps1" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Activează venv
|
||||
Write-Host "Activare virtual environment..." -ForegroundColor Cyan
|
||||
try {
|
||||
& $activateScript
|
||||
Write-Host "[OK] Virtual environment activat." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Nu am putut activa venv!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică dacă .env există
|
||||
$envFile = Join-Path $ProjectDir ".env"
|
||||
$envExample = Join-Path $ProjectDir ".env.example"
|
||||
|
||||
if (-not (Test-Path $envFile)) {
|
||||
Write-Host "[WARNING] Fisierul .env nu exista!" -ForegroundColor Yellow
|
||||
if (Test-Path $envExample) {
|
||||
Write-Host "Copiez din .env.example..." -ForegroundColor Yellow
|
||||
Copy-Item $envExample $envFile
|
||||
Write-Host ""
|
||||
Write-Host "IMPORTANT: Editeaza .env cu credentialele tale!" -ForegroundColor Yellow
|
||||
Start-Process notepad $envFile -Wait
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter dupa ce salvezi .env"
|
||||
} else {
|
||||
Write-Host "[ERROR] .env.example nu exista!" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Verifică directoare output
|
||||
$dataDir = Join-Path $ProjectDir "data"
|
||||
$logsDir = Join-Path $ProjectDir "logs"
|
||||
|
||||
if (-not (Test-Path $dataDir)) {
|
||||
New-Item -ItemType Directory -Path $dataDir | Out-Null
|
||||
}
|
||||
if (-not (Test-Path $logsDir)) {
|
||||
New-Item -ItemType Directory -Path $logsDir | Out-Null
|
||||
}
|
||||
|
||||
# Rulează scraper
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "Pornire scraper..." -ForegroundColor Yellow
|
||||
Write-Host "Browser-ul va fi vizibil pentru debugging." -ForegroundColor Gray
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
try {
|
||||
& python btgo_scraper.py
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Executie esuata: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "Executie terminata." -ForegroundColor Green
|
||||
Write-Host "Verifica rezultatele in:" -ForegroundColor Cyan
|
||||
Write-Host " - data\solduri_*.csv" -ForegroundColor Gray
|
||||
Write-Host " - data\tranzactii_*.csv" -ForegroundColor Gray
|
||||
Write-Host " - logs\scraper_*.log" -ForegroundColor Gray
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
81
deployment/windows/scripts/run_telegram_bot_manual.ps1
Normal file
81
deployment/windows/scripts/run_telegram_bot_manual.ps1
Normal file
@@ -0,0 +1,81 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Telegram Trigger Bot pentru BTGO - Rulare Manuala
|
||||
.DESCRIPTION
|
||||
Ruleaza Telegram bot-ul in modul manual (nu ca serviciu Windows)
|
||||
.NOTES
|
||||
Echivalent PowerShell pentru scripts\run_telegram_bot.bat
|
||||
Rulare: Right-click → "Run with PowerShell"
|
||||
Pentru a opri: Ctrl+C
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
|
||||
Set-Location $ProjectDir
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " TELEGRAM TRIGGER BOT PENTRU BTGO" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Working directory: $ProjectDir" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Verifică dacă venv există
|
||||
$venvPath = Join-Path $ProjectDir ".venv"
|
||||
$activateScript = Join-Path $venvPath "Scripts\Activate.ps1"
|
||||
|
||||
if (-not (Test-Path $activateScript)) {
|
||||
Write-Host "[ERROR] Virtual environment nu exista!" -ForegroundColor Red
|
||||
Write-Host "Ruleaza intai: setup_dev.ps1" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Activează venv
|
||||
Write-Host "Activare virtual environment..." -ForegroundColor Cyan
|
||||
try {
|
||||
& $activateScript
|
||||
Write-Host "[OK] Virtual environment activat." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Nu am putut activa venv!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică .env
|
||||
$envFile = Join-Path $ProjectDir ".env"
|
||||
|
||||
if (-not (Test-Path $envFile)) {
|
||||
Write-Host "[ERROR] Fisierul .env nu exista!" -ForegroundColor Red
|
||||
Write-Host "Configureaza .env cu TELEGRAM_BOT_TOKEN si TELEGRAM_ALLOWED_USER_IDS" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "Pornire Telegram Bot..." -ForegroundColor Yellow
|
||||
Write-Host "Bot-ul va astepta comenzi /scrape" -ForegroundColor Gray
|
||||
Write-Host "Apasa Ctrl+C pentru a opri bot-ul" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
try {
|
||||
& python telegram_trigger_bot.py
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "[ERROR] Executie esuata: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
172
deployment/windows/scripts/setup_dev.ps1
Normal file
172
deployment/windows/scripts/setup_dev.ps1
Normal file
@@ -0,0 +1,172 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Scraper - Setup Development Environment
|
||||
.DESCRIPTION
|
||||
Configureaza mediul de dezvoltare local (venv, dependente, browsere)
|
||||
.NOTES
|
||||
Echivalent PowerShell pentru scripts\setup_windows.bat
|
||||
Rulare: Right-click → "Run with PowerShell"
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
|
||||
Set-Location $ProjectDir
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " BTGO SCRAPER - SETUP WINDOWS" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Working directory: $ProjectDir" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Verifică Python
|
||||
try {
|
||||
$pythonVersion = & python --version 2>&1
|
||||
Write-Host "[OK] Python gasit: $pythonVersion" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Python nu este instalat!" -ForegroundColor Red
|
||||
Write-Host "Descarca Python de la: https://www.python.org/downloads/" -ForegroundColor Yellow
|
||||
Write-Host "Asigura-te ca bifezi 'Add Python to PATH' la instalare." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verifică dacă venv există deja
|
||||
$venvPath = Join-Path $ProjectDir ".venv"
|
||||
if (Test-Path (Join-Path $venvPath "Scripts\Activate.ps1")) {
|
||||
Write-Host "[INFO] Virtual environment exista deja." -ForegroundColor Yellow
|
||||
$recreate = Read-Host "Doresti sa il stergi si sa recreezi? (y/N)"
|
||||
if ($recreate -eq "y" -or $recreate -eq "Y") {
|
||||
Write-Host "Stergere venv existent..." -ForegroundColor Yellow
|
||||
Remove-Item -Path $venvPath -Recurse -Force
|
||||
} else {
|
||||
# Skip la activare
|
||||
goto ActivateVenv
|
||||
}
|
||||
}
|
||||
|
||||
# Creează virtual environment
|
||||
Write-Host "[STEP 1/5] Creare virtual environment..." -ForegroundColor Cyan
|
||||
try {
|
||||
& python -m venv .venv
|
||||
Write-Host "[OK] Virtual environment creat." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Nu am putut crea venv!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
:ActivateVenv
|
||||
# Activează venv
|
||||
Write-Host "[STEP 2/5] Activare virtual environment..." -ForegroundColor Cyan
|
||||
$activateScript = Join-Path $venvPath "Scripts\Activate.ps1"
|
||||
if (-not (Test-Path $activateScript)) {
|
||||
Write-Host "[ERROR] Nu am gasit scriptul de activare: $activateScript" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
try {
|
||||
& $activateScript
|
||||
Write-Host "[OK] Virtual environment activat." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Nu am putut activa venv!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Instalează requirements
|
||||
Write-Host "[STEP 3/5] Instalare dependente Python..." -ForegroundColor Cyan
|
||||
try {
|
||||
& pip install --upgrade pip --quiet
|
||||
& pip install -r requirements.txt
|
||||
Write-Host "[OK] Dependente instalate." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Instalare dependente esuata!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Instalează browsere Playwright
|
||||
Write-Host "[STEP 4/5] Instalare browsere Playwright (Chromium)..." -ForegroundColor Cyan
|
||||
Write-Host "Acest pas poate dura cateva minute..." -ForegroundColor Yellow
|
||||
try {
|
||||
& playwright install chromium
|
||||
Write-Host "[OK] Browsere instalate." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[ERROR] Instalare browsere esuata!" -ForegroundColor Red
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Copiază .env.example la .env dacă nu există
|
||||
Write-Host "[STEP 5/5] Verificare configuratie..." -ForegroundColor Cyan
|
||||
$envFile = Join-Path $ProjectDir ".env"
|
||||
$envExample = Join-Path $ProjectDir ".env.example"
|
||||
|
||||
if (-not (Test-Path $envFile)) {
|
||||
if (Test-Path $envExample) {
|
||||
Write-Host "Creare fisier .env din template..." -ForegroundColor Yellow
|
||||
Copy-Item $envExample $envFile
|
||||
Write-Host "[INFO] Fisierul .env a fost creat." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "IMPORTANT: Editeaza .env si completeaza:" -ForegroundColor Yellow
|
||||
Write-Host " - BTGO_USERNAME=your_username" -ForegroundColor Gray
|
||||
Write-Host " - BTGO_PASSWORD=your_password" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-Host "[ERROR] .env.example nu exista!" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
Write-Host "[OK] Fisierul .env exista deja." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Verifică directoarele
|
||||
$dataDir = Join-Path $ProjectDir "data"
|
||||
$logsDir = Join-Path $ProjectDir "logs"
|
||||
|
||||
if (-not (Test-Path $dataDir)) {
|
||||
New-Item -ItemType Directory -Path $dataDir | Out-Null
|
||||
}
|
||||
if (-not (Test-Path $logsDir)) {
|
||||
New-Item -ItemType Directory -Path $logsDir | Out-Null
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " SETUP COMPLET!" -ForegroundColor Green
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Next steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. Editeaza .env cu credentialele tale" -ForegroundColor Gray
|
||||
Write-Host " 2. Ruleaza scriptul run_scraper.ps1 sau run_telegram_bot.ps1" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "Pentru debugging selectors (optional):" -ForegroundColor Cyan
|
||||
Write-Host " .venv\Scripts\Activate.ps1" -ForegroundColor Gray
|
||||
Write-Host " playwright codegen https://btgo.ro --target python" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "Pentru detalii, vezi README.md" -ForegroundColor Gray
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
65
deployment/windows/scripts/start_service.ps1
Normal file
65
deployment/windows/scripts/start_service.ps1
Normal file
@@ -0,0 +1,65 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Start Service Script
|
||||
.DESCRIPTION
|
||||
Pornește serviciul Windows
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
|
||||
function Write-ColorOutput {
|
||||
param([string]$Message, [string]$Prefix = "")
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "START SERVICIU: $ServiceName" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Verifică dacă serviciul există
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $service) {
|
||||
Write-ColorOutput "Serviciul $ServiceName nu este instalat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Pornește serviciul
|
||||
Write-ColorOutput "Pornire serviciu..." -Prefix "[INFO]"
|
||||
try {
|
||||
Start-Service -Name $ServiceName
|
||||
Write-ColorOutput "Serviciu pornit cu succes!" -Prefix "[OK]"
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Verifică status
|
||||
$service = Get-Service -Name $ServiceName
|
||||
Write-Host ""
|
||||
Get-Service -Name $ServiceName | Format-Table -AutoSize
|
||||
|
||||
} catch {
|
||||
Write-ColorOutput "Pornirea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Write-Host "Eroare: $_" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host "Verificati logurile:" -ForegroundColor Yellow
|
||||
Write-Host " Get-Content logs\telegram_bot_stderr.log"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
101
deployment/windows/scripts/status.ps1
Normal file
101
deployment/windows/scripts/status.ps1
Normal file
@@ -0,0 +1,101 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Service Status Script
|
||||
.DESCRIPTION
|
||||
Afiseaza status detaliat al serviciului
|
||||
#>
|
||||
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$LogDir = Join-Path $ProjectDir "logs"
|
||||
|
||||
function Write-ColorOutput {
|
||||
param([string]$Message, [string]$Prefix = "")
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[AVERTIZARE]" { "Yellow" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message -ForegroundColor $color
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Separator {
|
||||
param([string]$Title)
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host $Title -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Separator "BTGO TELEGRAM BOT - STATUS SERVICIU"
|
||||
|
||||
# Verifică dacă serviciul există
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $service) {
|
||||
Write-ColorOutput "Serviciul $ServiceName NU este instalat" -Prefix "[INFO]"
|
||||
Write-Host ""
|
||||
Write-Host "Pentru instalare, rulati:" -ForegroundColor Yellow
|
||||
Write-Host " .\deploy.ps1"
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Afișează informații serviciu
|
||||
Write-Host "[STATUS SERVICIU]" -ForegroundColor Yellow
|
||||
$service | Format-Table -Property Status, Name, DisplayName, StartType -AutoSize
|
||||
|
||||
# Afișează configurație detaliată
|
||||
Write-Host "[CONFIGURATIE SERVICIU]" -ForegroundColor Yellow
|
||||
Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" |
|
||||
Format-List Name, DisplayName, State, StartMode, PathName
|
||||
|
||||
# Verifică ultimele logs
|
||||
$stdoutLog = Join-Path $LogDir "telegram_bot_stdout.log"
|
||||
if (Test-Path $stdoutLog) {
|
||||
Write-Host "[ULTIMELE 10 LINII DIN LOG]" -ForegroundColor Yellow
|
||||
Get-Content $stdoutLog -Tail 10
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-ColorOutput "Log stdout nu exista inca" -Prefix "[INFO]"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Verifică erori
|
||||
$stderrLog = Join-Path $LogDir "telegram_bot_stderr.log"
|
||||
if (Test-Path $stderrLog) {
|
||||
$errLines = (Get-Content $stderrLog).Count
|
||||
if ($errLines -gt 0) {
|
||||
Write-ColorOutput "Exista erori in stderr log ($errLines linii)" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Ultimele 5 erori:"
|
||||
Get-Content $stderrLog -Tail 5
|
||||
Write-Host ""
|
||||
}
|
||||
}
|
||||
|
||||
# Comenzi utile
|
||||
Write-Host "[COMENZI UTILE]" -ForegroundColor Yellow
|
||||
Write-Host " - Restart: .\restart_service.ps1"
|
||||
Write-Host " - View logs: .\view_logs.ps1"
|
||||
Write-Host " - Dezinstaleaza: .\uninstall_service.ps1"
|
||||
Write-Host " - Redeploy: .\deploy.ps1"
|
||||
Write-Host ""
|
||||
|
||||
# PowerShell Quick Commands
|
||||
Write-Host "[POWERSHELL COMMANDS]" -ForegroundColor Yellow
|
||||
Write-Host " - Restart-Service $ServiceName"
|
||||
Write-Host " - Stop-Service $ServiceName"
|
||||
Write-Host " - Start-Service $ServiceName"
|
||||
Write-Host " - Get-Content '$stdoutLog' -Wait -Tail 20"
|
||||
Write-Host ""
|
||||
|
||||
Read-Host "Apasa Enter pentru a inchide"
|
||||
133
deployment/windows/scripts/uninstall_service.ps1
Normal file
133
deployment/windows/scripts/uninstall_service.ps1
Normal file
@@ -0,0 +1,133 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - Service Uninstall Script
|
||||
.DESCRIPTION
|
||||
Dezinstaleaza serviciul Windows BTGO Telegram Bot
|
||||
.NOTES
|
||||
Rulare: Right-click → "Run with PowerShell" (ca Administrator)
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$NssmPath = Join-Path $ProjectDir "deployment\windows\tools\nssm.exe"
|
||||
|
||||
function Write-ColorOutput {
|
||||
param([string]$Message, [string]$Prefix = "")
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
"[AVERTIZARE]" { "Yellow" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Separator {
|
||||
param([string]$Title = "")
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
if ($Title) {
|
||||
Write-Host $Title -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# VERIFICARE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-Separator "DEZINSTALARE SERVICIU: $ServiceName"
|
||||
Write-Host ""
|
||||
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $service) {
|
||||
Write-ColorOutput "Serviciul $ServiceName nu este instalat." -Prefix "[INFO]"
|
||||
Write-Host "Nimic de dezinstalat."
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# OPRIRE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-ColorOutput "Verificare status serviciu..." -Prefix "[INFO]"
|
||||
|
||||
if ($service.Status -eq "Running") {
|
||||
Write-ColorOutput "Serviciul ruleaza. Oprire..." -Prefix "[INFO]"
|
||||
try {
|
||||
Stop-Service -Name $ServiceName -Force
|
||||
Write-ColorOutput "Serviciu oprit cu succes" -Prefix "[OK]"
|
||||
Start-Sleep -Seconds 2
|
||||
} catch {
|
||||
Write-ColorOutput "Oprirea serviciului a esuat!" -Prefix "[AVERTIZARE]"
|
||||
Write-ColorOutput "Incercam oprire fortata..." -Prefix "[INFO]"
|
||||
sc.exe stop $ServiceName | Out-Null
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
} else {
|
||||
Write-ColorOutput "Serviciul este deja oprit" -Prefix "[INFO]"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# DEZINSTALARE SERVICIU
|
||||
# ============================================================================
|
||||
|
||||
Write-ColorOutput "Dezinstalare serviciu..." -Prefix "[INFO]"
|
||||
|
||||
try {
|
||||
if (Test-Path $NssmPath) {
|
||||
# Folosește NSSM pentru dezinstalare
|
||||
& $NssmPath remove $ServiceName confirm
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
} else {
|
||||
# Fallback la sc delete
|
||||
Write-ColorOutput "NSSM nu a fost gasit, folosim sc delete" -Prefix "[INFO]"
|
||||
sc.exe delete $ServiceName | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
}
|
||||
} catch {
|
||||
Write-ColorOutput "Dezinstalarea serviciului a esuat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# VERIFICARE FINALA
|
||||
# ============================================================================
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$serviceCheck = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $serviceCheck) {
|
||||
Write-Separator "[SUCCES] SERVICIU DEZINSTALAT CU SUCCES!"
|
||||
Write-Host ""
|
||||
Write-Host "Serviciul $ServiceName a fost eliminat din sistem." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Pentru a reinstala serviciul, rulati:" -ForegroundColor Yellow
|
||||
Write-Host " .\install_service.ps1"
|
||||
Write-Host ""
|
||||
Write-Separator
|
||||
} else {
|
||||
Write-ColorOutput "Serviciul pare sa mai existe in sistem!" -Prefix "[AVERTIZARE]"
|
||||
Write-Host "Poate fi nevoie de un restart al sistemului."
|
||||
Get-Service -Name $ServiceName | Format-List
|
||||
}
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
103
deployment/windows/scripts/update_browsers.ps1
Normal file
103
deployment/windows/scripts/update_browsers.ps1
Normal file
@@ -0,0 +1,103 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Update Playwright Browsers (Global Location)
|
||||
.DESCRIPTION
|
||||
Actualizeaza browserele Playwright in locatia globala
|
||||
Foloseste aceasta comanda pentru update-uri de browsere
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ServiceName = "BTGOTelegramBot"
|
||||
|
||||
function Write-ColorOutput {
|
||||
param([string]$Message, [string]$Prefix = "")
|
||||
$color = switch ($Prefix) {
|
||||
"[OK]" { "Green" }
|
||||
"[INFO]" { "Cyan" }
|
||||
"[EROARE]" { "Red" }
|
||||
"[AVERTIZARE]" { "Yellow" }
|
||||
default { "White" }
|
||||
}
|
||||
if ($Prefix) {
|
||||
Write-Host "$Prefix " -ForegroundColor $color -NoNewline
|
||||
Write-Host $Message
|
||||
} else {
|
||||
Write-Host $Message -ForegroundColor $color
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "UPDATE PLAYWRIGHT BROWSERS" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Verifică environment variable
|
||||
$globalBrowserPath = [System.Environment]::GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH", [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
if (-not $globalBrowserPath) {
|
||||
$globalBrowserPath = "C:\playwright-browsers"
|
||||
Write-ColorOutput "PLAYWRIGHT_BROWSERS_PATH nu este setat. Folosim: $globalBrowserPath" -Prefix "[AVERTIZARE]"
|
||||
|
||||
# Setează environment variable
|
||||
[System.Environment]::SetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH", $globalBrowserPath, [System.EnvironmentVariableTarget]::Machine)
|
||||
Write-ColorOutput "Environment variable setata" -Prefix "[OK]"
|
||||
}
|
||||
|
||||
$env:PLAYWRIGHT_BROWSERS_PATH = $globalBrowserPath
|
||||
Write-ColorOutput "Locatie browsere: $globalBrowserPath" -Prefix "[INFO]"
|
||||
|
||||
# Creează director dacă nu există
|
||||
if (-not (Test-Path $globalBrowserPath)) {
|
||||
New-Item -ItemType Directory -Path $globalBrowserPath -Force | Out-Null
|
||||
icacls $globalBrowserPath /grant "SYSTEM:(OI)(CI)F" /T 2>&1 | Out-Null
|
||||
Write-ColorOutput "Director creat cu permisiuni SYSTEM" -Prefix "[OK]"
|
||||
}
|
||||
|
||||
# Update browsere
|
||||
Write-Host ""
|
||||
Write-ColorOutput "Instalare/Update browsere Playwright..." -Prefix "[INFO]"
|
||||
Write-Host "Aceasta poate dura 1-2 minute..." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
try {
|
||||
python -m playwright install chromium
|
||||
|
||||
if ($LASTEXITCODE -ne 0) { throw }
|
||||
Write-Host ""
|
||||
Write-ColorOutput "Browsere actualizate cu succes!" -Prefix "[OK]"
|
||||
} catch {
|
||||
Write-ColorOutput "Update-ul browserelor a esuat!" -Prefix "[EROARE]"
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Restart serviciu dacă rulează
|
||||
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
|
||||
if ($service) {
|
||||
Write-Host ""
|
||||
Write-ColorOutput "Restart serviciu..." -Prefix "[INFO]"
|
||||
|
||||
if ($service.Status -eq "Running") {
|
||||
Restart-Service -Name $ServiceName -Force
|
||||
Start-Sleep -Seconds 3
|
||||
|
||||
$service = Get-Service -Name $ServiceName
|
||||
if ($service.Status -eq "Running") {
|
||||
Write-ColorOutput "Serviciu restartat cu succes!" -Prefix "[OK]"
|
||||
} else {
|
||||
Write-ColorOutput "Serviciul nu a pornit" -Prefix "[AVERTIZARE]"
|
||||
}
|
||||
} else {
|
||||
Write-ColorOutput "Serviciul nu rula" -Prefix "[INFO]"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Green
|
||||
Write-Host "[SUCCES] BROWSERE ACTUALIZATE!" -ForegroundColor Green
|
||||
Write-Host ("=" * 80) -ForegroundColor Green
|
||||
|
||||
Read-Host "`nApasa Enter pentru a inchide"
|
||||
137
deployment/windows/scripts/view_logs.ps1
Normal file
137
deployment/windows/scripts/view_logs.ps1
Normal file
@@ -0,0 +1,137 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
BTGO Telegram Bot - View Logs Script
|
||||
.DESCRIPTION
|
||||
Afiseaza logurile serviciului in timp real
|
||||
#>
|
||||
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$ProjectDir = Resolve-Path (Join-Path $ScriptDir "..\..\..") | Select-Object -ExpandProperty Path
|
||||
$LogDir = Join-Path $ProjectDir "logs"
|
||||
$StdoutLog = Join-Path $LogDir "telegram_bot_stdout.log"
|
||||
$StderrLog = Join-Path $LogDir "telegram_bot_stderr.log"
|
||||
|
||||
function Show-Menu {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host " BTGO TELEGRAM BOT - VIEWER LOGS" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Logs directory: $LogDir" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Selecteaza optiune:" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " 1. Vizualizare stdout (output normal)"
|
||||
Write-Host " 2. Vizualizare stderr (erori)"
|
||||
Write-Host " 3. Tail stdout (timp real - ultim 30 linii)"
|
||||
Write-Host " 4. Tail stderr (timp real - ultim 30 linii)"
|
||||
Write-Host " 5. Deschide director logs in Explorer"
|
||||
Write-Host " 6. Sterge toate logurile"
|
||||
Write-Host " 0. Iesire"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
function View-Stdout {
|
||||
Clear-Host
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STDOUT LOG" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
if (Test-Path $StdoutLog) {
|
||||
Get-Content $StdoutLog
|
||||
} else {
|
||||
Write-Host "[INFO] Log-ul nu exista inca" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function View-Stderr {
|
||||
Clear-Host
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STDERR LOG" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
if (Test-Path $StderrLog) {
|
||||
Get-Content $StderrLog
|
||||
} else {
|
||||
Write-Host "[INFO] Log-ul nu exista inca" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ""
|
||||
Read-Host "Apasa Enter pentru a reveni la meniu"
|
||||
}
|
||||
|
||||
function Tail-Stdout {
|
||||
Clear-Host
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STDOUT TAIL - TIMP REAL (Apasa Ctrl+C pentru a opri)" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
if (Test-Path $StdoutLog) {
|
||||
Get-Content $StdoutLog -Wait -Tail 30
|
||||
} else {
|
||||
Write-Host "[INFO] Log-ul nu exista inca" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
function Tail-Stderr {
|
||||
Clear-Host
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host "STDERR TAIL - TIMP REAL (Apasa Ctrl+C pentru a opri)" -ForegroundColor Yellow
|
||||
Write-Host ("=" * 80) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
if (Test-Path $StderrLog) {
|
||||
Get-Content $StderrLog -Wait -Tail 30
|
||||
} else {
|
||||
Write-Host "[INFO] Log-ul nu exista inca" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
function Open-LogsExplorer {
|
||||
if (Test-Path $LogDir) {
|
||||
explorer $LogDir
|
||||
} else {
|
||||
Write-Host "[INFO] Directorul logs nu exista" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
function Clear-AllLogs {
|
||||
Write-Host ""
|
||||
Write-Host "[AVERTIZARE] Stergi TOATE logurile?" -ForegroundColor Yellow
|
||||
$confirm = Read-Host "Confirma (Y/N)"
|
||||
if ($confirm -eq "Y" -or $confirm -eq "y") {
|
||||
Remove-Item (Join-Path $LogDir "*.log") -Force -ErrorAction SilentlyContinue
|
||||
Write-Host "[OK] Loguri sterse" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Anulat" -ForegroundColor Cyan
|
||||
}
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
|
||||
# Main loop
|
||||
do {
|
||||
Show-Menu
|
||||
$choice = Read-Host "Optiune (0-6)"
|
||||
|
||||
switch ($choice) {
|
||||
"1" { View-Stdout }
|
||||
"2" { View-Stderr }
|
||||
"3" { Tail-Stdout }
|
||||
"4" { Tail-Stderr }
|
||||
"5" { Open-LogsExplorer }
|
||||
"6" { Clear-AllLogs }
|
||||
"0" {
|
||||
Write-Host ""
|
||||
Write-Host "Bye!" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
default {
|
||||
Write-Host "[EROARE] Optiune invalida!" -ForegroundColor Red
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
}
|
||||
} while ($true)
|
||||
2
deployment/windows/tools/.gitignore
vendored
Normal file
2
deployment/windows/tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# NSSM binaries (downloaded automatically)
|
||||
nssm.exe
|
||||
31
docker-compose.yml
Normal file
31
docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
btgo-scraper:
|
||||
build: .
|
||||
container_name: btgo-scraper
|
||||
|
||||
# Mount volumes pentru persistenta
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./logs:/app/logs
|
||||
|
||||
# Environment variables - citeste din .env
|
||||
environment:
|
||||
- BTGO_USERNAME=${BTGO_USERNAME}
|
||||
- BTGO_PASSWORD=${BTGO_PASSWORD}
|
||||
- HEADLESS=true
|
||||
- TIMEOUT_2FA_SECONDS=${TIMEOUT_2FA_SECONDS:-120}
|
||||
- SCREENSHOT_ON_ERROR=${SCREENSHOT_ON_ERROR:-true}
|
||||
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
||||
- TZ=Europe/Bucharest
|
||||
|
||||
# Restart policy (schimba la 'always' pentru rulare automata)
|
||||
restart: "no"
|
||||
|
||||
# Resource limits (optional)
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
123
get_telegram_chat_id.py
Normal file
123
get_telegram_chat_id.py
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Helper script pentru obținerea Chat ID Telegram (pentru DM și grupuri)
|
||||
"""
|
||||
|
||||
import os
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment
|
||||
load_dotenv()
|
||||
|
||||
BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
|
||||
|
||||
if not BOT_TOKEN:
|
||||
print("❌ TELEGRAM_BOT_TOKEN nu este setat în .env!")
|
||||
print("\nAdaugă în .env:")
|
||||
print("TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrs")
|
||||
exit(1)
|
||||
|
||||
def get_updates():
|
||||
"""Preia ultimele update-uri de la bot"""
|
||||
url = f"https://api.telegram.org/bot{BOT_TOKEN}/getUpdates"
|
||||
response = requests.get(url)
|
||||
return response.json()
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" Telegram Chat ID Helper")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("📱 Instrucțiuni:")
|
||||
print("1. Trimite /start către bot (DM)")
|
||||
print(" SAU")
|
||||
print("2. Trimite /start în grupul unde ai bot-ul")
|
||||
print()
|
||||
print("Apasă Enter după ce ai trimis mesajul...")
|
||||
input()
|
||||
|
||||
print("\n🔍 Căutare mesaje...")
|
||||
data = get_updates()
|
||||
|
||||
if not data.get('ok'):
|
||||
print(f"❌ Eroare API: {data}")
|
||||
return
|
||||
|
||||
results = data.get('result', [])
|
||||
if not results:
|
||||
print("❌ Niciun mesaj găsit!")
|
||||
print("\nAsigură-te că:")
|
||||
print("• Ai trimis /start către bot SAU în grup")
|
||||
print("• Bot-ul este adăugat în grup (dacă folosești grup)")
|
||||
print("• Token-ul este corect")
|
||||
return
|
||||
|
||||
print(f"\n✅ Găsit {len(results)} mesaje!\n")
|
||||
print("=" * 60)
|
||||
|
||||
# Procesează ultimele mesaje
|
||||
seen_chats = {}
|
||||
|
||||
for update in results:
|
||||
if 'message' in update:
|
||||
msg = update['message']
|
||||
chat = msg['chat']
|
||||
chat_id = chat['id']
|
||||
chat_type = chat['type']
|
||||
|
||||
# Evită duplicate
|
||||
if chat_id in seen_chats:
|
||||
continue
|
||||
|
||||
seen_chats[chat_id] = True
|
||||
|
||||
# Detalii chat
|
||||
if chat_type == 'private':
|
||||
# DM
|
||||
user = msg['from']
|
||||
print(f"📱 DM cu {user.get('first_name', 'Unknown')}")
|
||||
print(f" User ID: {user['id']}")
|
||||
print(f" Username: @{user.get('username', 'N/A')}")
|
||||
print(f" Chat ID: {chat_id}")
|
||||
elif chat_type in ['group', 'supergroup']:
|
||||
# Grup
|
||||
print(f"👥 Grup: {chat.get('title', 'Unknown')}")
|
||||
print(f" Chat ID: {chat_id} ⚠️ NEGATIV pentru grupuri!")
|
||||
print(f" Tip: {chat_type}")
|
||||
# User care a trimis mesajul
|
||||
user = msg['from']
|
||||
print(f" Mesaj de la: @{user.get('username', 'Unknown')} (ID: {user['id']})")
|
||||
|
||||
print()
|
||||
|
||||
print("=" * 60)
|
||||
print("\n💡 Pentru configurare .env:")
|
||||
print()
|
||||
|
||||
# Recomandări
|
||||
for chat_id, chat_data in seen_chats.items():
|
||||
if chat_id < 0: # Grup
|
||||
print(f"# Pentru grup (notificări + comenzi):")
|
||||
print(f"TELEGRAM_CHAT_ID={chat_id}")
|
||||
else: # DM
|
||||
print(f"# Pentru DM:")
|
||||
print(f"TELEGRAM_CHAT_ID={chat_id}")
|
||||
|
||||
print()
|
||||
print("# User IDs autorizați (pot rula /scrape):")
|
||||
print("TELEGRAM_ALLOWED_USER_IDS=", end="")
|
||||
|
||||
# Colectează user IDs unice
|
||||
user_ids = set()
|
||||
for update in results:
|
||||
if 'message' in update:
|
||||
user_id = update['message']['from']['id']
|
||||
user_ids.add(user_id)
|
||||
|
||||
print(",".join(str(uid) for uid in sorted(user_ids)))
|
||||
print()
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1
logs/.gitkeep
Normal file
1
logs/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# Folder pentru log files
|
||||
504
notifications.py
Normal file
504
notifications.py
Normal file
@@ -0,0 +1,504 @@
|
||||
"""
|
||||
Notification module for BTGO Scraper
|
||||
Handles email and Discord notifications with file attachments
|
||||
"""
|
||||
|
||||
import smtplib
|
||||
import logging
|
||||
import zipfile
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
import requests
|
||||
|
||||
|
||||
class EmailNotifier:
|
||||
"""Handles email notifications via SMTP"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.enabled = config.EMAIL_ENABLED
|
||||
|
||||
def send(self, files: List[str], account_count: int) -> bool:
|
||||
"""
|
||||
Send email with CSV attachments
|
||||
|
||||
Args:
|
||||
files: List of file paths to attach
|
||||
account_count: Number of accounts processed
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if not self.enabled:
|
||||
logging.info("Email notifications disabled")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Validate config
|
||||
if not all([self.config.SMTP_SERVER, self.config.EMAIL_FROM, self.config.EMAIL_TO]):
|
||||
logging.error("Email configuration incomplete")
|
||||
return False
|
||||
|
||||
# Create message
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = self.config.EMAIL_FROM
|
||||
msg['To'] = self.config.EMAIL_TO
|
||||
msg['Subject'] = f'BTGO Scraper Results - {datetime.now().strftime("%Y-%m-%d %H:%M")}'
|
||||
|
||||
# Email body
|
||||
body = self._create_email_body(files, account_count)
|
||||
msg.attach(MIMEText(body, 'html'))
|
||||
|
||||
# Attach files
|
||||
total_size = 0
|
||||
for file_path in files:
|
||||
if not Path(file_path).exists():
|
||||
logging.warning(f"File not found for email: {file_path}")
|
||||
continue
|
||||
|
||||
file_size = Path(file_path).stat().st_size
|
||||
total_size += file_size
|
||||
|
||||
# Check size limit (10MB typical SMTP limit)
|
||||
if total_size > 10 * 1024 * 1024:
|
||||
logging.warning(f"Email attachments exceed 10MB, creating ZIP archive")
|
||||
return self._send_with_zip(files, account_count)
|
||||
|
||||
with open(file_path, 'rb') as f:
|
||||
part = MIMEBase('application', 'octet-stream')
|
||||
part.set_payload(f.read())
|
||||
encoders.encode_base64(part)
|
||||
part.add_header(
|
||||
'Content-Disposition',
|
||||
f'attachment; filename={Path(file_path).name}'
|
||||
)
|
||||
msg.attach(part)
|
||||
|
||||
# Send email
|
||||
logging.info(f"Sending email to {self.config.EMAIL_TO}...")
|
||||
with smtplib.SMTP(self.config.SMTP_SERVER, self.config.SMTP_PORT) as server:
|
||||
server.starttls()
|
||||
if self.config.SMTP_USERNAME and self.config.SMTP_PASSWORD:
|
||||
server.login(self.config.SMTP_USERNAME, self.config.SMTP_PASSWORD)
|
||||
server.send_message(msg)
|
||||
|
||||
logging.info(f"✓ Email sent successfully to {self.config.EMAIL_TO}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send email: {e}")
|
||||
return False
|
||||
|
||||
def _send_with_zip(self, files: List[str], account_count: int) -> bool:
|
||||
"""Send email with files compressed as ZIP"""
|
||||
try:
|
||||
# Create ZIP archive
|
||||
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||
zip_path = Path(self.config.OUTPUT_DIR) / f'btgo_export_{timestamp}.zip'
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
for file_path in files:
|
||||
if Path(file_path).exists():
|
||||
zipf.write(file_path, Path(file_path).name)
|
||||
|
||||
logging.info(f"Created ZIP archive: {zip_path}")
|
||||
|
||||
# Send with ZIP attachment instead
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = self.config.EMAIL_FROM
|
||||
msg['To'] = self.config.EMAIL_TO
|
||||
msg['Subject'] = f'BTGO Scraper Results (ZIP) - {datetime.now().strftime("%Y-%m-%d %H:%M")}'
|
||||
|
||||
body = self._create_email_body([str(zip_path)], account_count, is_zip=True)
|
||||
msg.attach(MIMEText(body, 'html'))
|
||||
|
||||
with open(zip_path, 'rb') as f:
|
||||
part = MIMEBase('application', 'zip')
|
||||
part.set_payload(f.read())
|
||||
encoders.encode_base64(part)
|
||||
part.add_header('Content-Disposition', f'attachment; filename={zip_path.name}')
|
||||
msg.attach(part)
|
||||
|
||||
with smtplib.SMTP(self.config.SMTP_SERVER, self.config.SMTP_PORT) as server:
|
||||
server.starttls()
|
||||
if self.config.SMTP_USERNAME and self.config.SMTP_PASSWORD:
|
||||
server.login(self.config.SMTP_USERNAME, self.config.SMTP_PASSWORD)
|
||||
server.send_message(msg)
|
||||
|
||||
logging.info(f"✓ Email with ZIP sent successfully to {self.config.EMAIL_TO}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send email with ZIP: {e}")
|
||||
return False
|
||||
|
||||
def _create_email_body(self, files: List[str], account_count: int, is_zip: bool = False) -> str:
|
||||
"""Create HTML email body"""
|
||||
file_list = '<br>'.join([f'• {Path(f).name}' for f in files])
|
||||
file_count = 1 if is_zip else len(files)
|
||||
|
||||
return f"""
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif;">
|
||||
<h2 style="color: #2c3e50;">BTGO Scraper Results</h2>
|
||||
<p><strong>Execution time:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
<p><strong>Accounts processed:</strong> {account_count}</p>
|
||||
<p><strong>Files attached:</strong> {file_count}</p>
|
||||
<hr>
|
||||
<h3>{'Archive contents:' if is_zip else 'Attached files:'}</h3>
|
||||
{file_list}
|
||||
<hr>
|
||||
<p style="color: #7f8c8d; font-size: 12px;">
|
||||
This email was automatically generated by BTGO Scraper.<br>
|
||||
For issues, check the logs at your deployment location.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
class TelegramNotifier:
|
||||
"""Handles Telegram notifications via Bot API"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.enabled = config.TELEGRAM_ENABLED
|
||||
self.bot_token = config.TELEGRAM_BOT_TOKEN
|
||||
self.chat_id = config.TELEGRAM_CHAT_ID
|
||||
self.max_file_size = 50 * 1024 * 1024 # 50MB Telegram limit
|
||||
|
||||
def send(self, files: List[str], accounts: list, telegram_message_id: str = None, telegram_chat_id: str = None) -> bool:
|
||||
"""
|
||||
Send Telegram message with file attachments
|
||||
|
||||
Args:
|
||||
files: List of file paths to attach
|
||||
accounts: List of account data with balances
|
||||
telegram_message_id: Message ID to edit (for progress updates)
|
||||
telegram_chat_id: Chat ID to edit (for progress updates)
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if not self.enabled:
|
||||
logging.info("Telegram notifications disabled")
|
||||
return False
|
||||
|
||||
try:
|
||||
if not self.bot_token or not self.chat_id:
|
||||
logging.error("Telegram bot token or chat ID not configured")
|
||||
return False
|
||||
|
||||
# Store message IDs for editing
|
||||
self.progress_message_id = telegram_message_id
|
||||
self.progress_chat_id = telegram_chat_id or self.chat_id
|
||||
logging.info(f"Received telegram_message_id: {telegram_message_id}, telegram_chat_id: {telegram_chat_id}")
|
||||
logging.info(f"Stored progress_message_id: {self.progress_message_id}, progress_chat_id: {self.progress_chat_id}")
|
||||
|
||||
# Check total file size
|
||||
total_size = sum(Path(f).stat().st_size for f in files if Path(f).exists())
|
||||
|
||||
if total_size > self.max_file_size:
|
||||
logging.warning(f"Files exceed Telegram 50MB limit, creating ZIP archive")
|
||||
return self._send_with_zip(files, accounts)
|
||||
else:
|
||||
return self._send_files(files, accounts)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send Telegram notification: {e}")
|
||||
return False
|
||||
|
||||
def _send_message(self, text: str) -> bool:
|
||||
"""Send text message to Telegram"""
|
||||
try:
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/sendMessage"
|
||||
payload = {
|
||||
'chat_id': self.chat_id,
|
||||
'text': text,
|
||||
'parse_mode': 'HTML'
|
||||
}
|
||||
response = requests.post(url, json=payload)
|
||||
return response.status_code == 200
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send Telegram message: {e}")
|
||||
return False
|
||||
|
||||
def _edit_message(self, chat_id: str, message_id: str, text: str) -> bool:
|
||||
"""Edit existing Telegram message (for progress updates)"""
|
||||
try:
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/editMessageText"
|
||||
payload = {
|
||||
'chat_id': chat_id,
|
||||
'message_id': message_id,
|
||||
'text': text,
|
||||
'parse_mode': 'Markdown'
|
||||
}
|
||||
response = requests.post(url, json=payload)
|
||||
return response.status_code == 200
|
||||
|
||||
except Exception as e:
|
||||
# Silent fail pentru progress updates - nu blochez scraper-ul
|
||||
logging.debug(f"Progress update failed: {e}")
|
||||
return False
|
||||
|
||||
def _send_files(self, files: List[str], accounts: list) -> bool:
|
||||
"""Send files directly to Telegram"""
|
||||
try:
|
||||
# Calculate total balance
|
||||
total_ron = sum(acc['sold'] for acc in accounts if acc.get('moneda') == 'RON')
|
||||
|
||||
# Build message with account balances
|
||||
message = f"<b>BTGO SCRAPER - FINALIZAT CU SUCCES</b>\n\n"
|
||||
message += f"Timp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
message += f"Conturi procesate: {len(accounts)}\n"
|
||||
message += f"Fisiere: {len(files)}\n\n"
|
||||
|
||||
message += "<b>SOLDURI:</b>\n"
|
||||
for acc in accounts:
|
||||
nume = acc['nume_cont']
|
||||
sold = acc['sold']
|
||||
moneda = acc['moneda']
|
||||
message += f"{nume}: {sold:,.2f} {moneda}\n"
|
||||
message += f"\n<b>TOTAL: {total_ron:,.2f} RON</b>\n"
|
||||
|
||||
logging.info(f"Sending Telegram notification to chat {self.progress_chat_id}...")
|
||||
logging.info(f"Progress message_id: {self.progress_message_id}, chat_id: {self.progress_chat_id}")
|
||||
|
||||
# Edit initial message or send new one
|
||||
if self.progress_message_id and self.progress_chat_id:
|
||||
# Edit the initial progress message
|
||||
logging.info(f"Editing initial message {self.progress_message_id} in chat {self.progress_chat_id}")
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/editMessageText"
|
||||
payload = {
|
||||
'chat_id': self.progress_chat_id,
|
||||
'message_id': self.progress_message_id,
|
||||
'text': message,
|
||||
'parse_mode': 'HTML'
|
||||
}
|
||||
response = requests.post(url, json=payload)
|
||||
if response.status_code == 200:
|
||||
logging.info("✓ Message edited successfully with balances")
|
||||
else:
|
||||
logging.error(f"Failed to edit message: {response.status_code} - {response.text}")
|
||||
else:
|
||||
# Send new message if no message_id provided
|
||||
logging.warning("No progress_message_id provided, sending new message")
|
||||
self._send_message(message)
|
||||
|
||||
# Send each file as document
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/sendDocument"
|
||||
success_count = 0
|
||||
|
||||
for file_path in files:
|
||||
if not Path(file_path).exists():
|
||||
logging.warning(f"File not found: {file_path}")
|
||||
continue
|
||||
|
||||
file_size = Path(file_path).stat().st_size
|
||||
if file_size > self.max_file_size:
|
||||
logging.warning(f"File too large for Telegram: {Path(file_path).name} ({file_size / 1024 / 1024:.2f} MB)")
|
||||
continue
|
||||
|
||||
with open(file_path, 'rb') as f:
|
||||
files_data = {'document': f}
|
||||
data = {
|
||||
'chat_id': self.chat_id,
|
||||
'caption': f"{Path(file_path).name}"
|
||||
}
|
||||
response = requests.post(url, data=data, files=files_data)
|
||||
|
||||
if response.status_code == 200:
|
||||
logging.info(f"✓ Sent to Telegram: {Path(file_path).name}")
|
||||
success_count += 1
|
||||
else:
|
||||
logging.error(f"Failed to send {Path(file_path).name}: {response.text}")
|
||||
|
||||
if success_count > 0:
|
||||
logging.info(f"✓ Telegram notification sent successfully ({success_count}/{len(files)} files)")
|
||||
return True
|
||||
else:
|
||||
logging.error("No files were sent to Telegram")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send Telegram files: {e}")
|
||||
return False
|
||||
|
||||
def _send_with_zip(self, files: List[str], accounts: list) -> bool:
|
||||
"""Send files as ZIP archive to Telegram"""
|
||||
try:
|
||||
# Create ZIP archive
|
||||
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||
zip_path = Path(self.config.OUTPUT_DIR) / f'btgo_export_{timestamp}.zip'
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
for file_path in files:
|
||||
if Path(file_path).exists():
|
||||
zipf.write(file_path, Path(file_path).name)
|
||||
|
||||
zip_size = Path(zip_path).stat().st_size
|
||||
logging.info(f"Created ZIP archive: {zip_path} ({zip_size / 1024 / 1024:.2f} MB)")
|
||||
|
||||
# Check if ZIP is still too large
|
||||
if zip_size > self.max_file_size:
|
||||
logging.error(f"ZIP archive exceeds Telegram 50MB limit ({zip_size / 1024 / 1024:.2f} MB)")
|
||||
# Send message without attachment
|
||||
return self._send_message_only(accounts, files)
|
||||
|
||||
# Calculate total balance
|
||||
total_ron = sum(acc['sold'] for acc in accounts if acc.get('moneda') == 'RON')
|
||||
|
||||
# Build message with balances
|
||||
message = f"<b>BTGO SCRAPER - FINALIZAT CU SUCCES (ARHIVA ZIP)</b>\n\n"
|
||||
message += f"Timp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
message += f"Conturi procesate: {len(accounts)}\n"
|
||||
message += f"Dimensiune arhiva: {zip_size / 1024 / 1024:.2f} MB\n"
|
||||
message += f"Fisiere in arhiva: {len(files)}\n\n"
|
||||
|
||||
message += "<b>SOLDURI:</b>\n"
|
||||
for acc in accounts:
|
||||
nume = acc['nume_cont']
|
||||
sold = acc['sold']
|
||||
moneda = acc['moneda']
|
||||
message += f"{nume}: {sold:,.2f} {moneda}\n"
|
||||
message += f"\n<b>TOTAL: {total_ron:,.2f} RON</b>\n"
|
||||
|
||||
# Edit initial message or send new one
|
||||
if self.progress_message_id and self.progress_chat_id:
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/editMessageText"
|
||||
payload = {
|
||||
'chat_id': self.progress_chat_id,
|
||||
'message_id': self.progress_message_id,
|
||||
'text': message,
|
||||
'parse_mode': 'HTML'
|
||||
}
|
||||
response = requests.post(url, json=payload)
|
||||
if response.status_code != 200:
|
||||
logging.error(f"Failed to edit message: {response.text}")
|
||||
else:
|
||||
self._send_message(message)
|
||||
|
||||
# Send ZIP file
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/sendDocument"
|
||||
with open(zip_path, 'rb') as f:
|
||||
files_data = {'document': f}
|
||||
data = {
|
||||
'chat_id': self.chat_id,
|
||||
'caption': f"BTGO Export Archive - {timestamp}"
|
||||
}
|
||||
response = requests.post(url, data=data, files=files_data)
|
||||
|
||||
if response.status_code == 200:
|
||||
logging.info(f"✓ Telegram notification with ZIP sent successfully")
|
||||
return True
|
||||
else:
|
||||
logging.error(f"Telegram send failed: {response.status_code} - {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send Telegram ZIP: {e}")
|
||||
return False
|
||||
|
||||
def _send_message_only(self, accounts: list, files: List[str]) -> bool:
|
||||
"""Send Telegram message without files (when too large)"""
|
||||
try:
|
||||
file_list = '\n'.join([f'{Path(f).name} ({Path(f).stat().st_size / 1024:.1f} KB)' for f in files if Path(f).exists()])
|
||||
|
||||
# Calculate total balance
|
||||
total_ron = sum(acc['sold'] for acc in accounts if acc.get('moneda') == 'RON')
|
||||
|
||||
message = f"<b>BTGO SCRAPER - FINALIZAT CU SUCCES</b>\n\n"
|
||||
message += f"Timp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
message += f"Conturi procesate: {len(accounts)}\n\n"
|
||||
|
||||
message += "<b>SOLDURI:</b>\n"
|
||||
for acc in accounts:
|
||||
nume = acc['nume_cont']
|
||||
sold = acc['sold']
|
||||
moneda = acc['moneda']
|
||||
message += f"{nume}: {sold:,.2f} {moneda}\n"
|
||||
message += f"\n<b>TOTAL: {total_ron:,.2f} RON</b>\n\n"
|
||||
|
||||
message += f"<b>ATENTIE:</b> Fisiere prea mari pentru Telegram\n\n"
|
||||
message += f"<b>Fisiere generate:</b>\n{file_list}\n\n"
|
||||
message += f"Verifica locatia de deployment pentru fisiere."
|
||||
|
||||
# Edit initial message or send new one
|
||||
if self.progress_message_id and self.progress_chat_id:
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/editMessageText"
|
||||
payload = {
|
||||
'chat_id': self.progress_chat_id,
|
||||
'message_id': self.progress_message_id,
|
||||
'text': message,
|
||||
'parse_mode': 'HTML'
|
||||
}
|
||||
response = requests.post(url, json=payload)
|
||||
return response.status_code == 200
|
||||
else:
|
||||
return self._send_message(message)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send Telegram message: {e}")
|
||||
return False
|
||||
|
||||
|
||||
class NotificationService:
|
||||
"""Orchestrates all notification channels"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.email = EmailNotifier(config)
|
||||
self.telegram = TelegramNotifier(config)
|
||||
|
||||
def send_all(self, files: List[str], accounts: list, telegram_message_id: str = None, telegram_chat_id: str = None) -> dict:
|
||||
"""
|
||||
Send notifications via all enabled channels
|
||||
|
||||
Args:
|
||||
files: List of file paths to send
|
||||
accounts: List of account data with balances
|
||||
telegram_message_id: Message ID to edit (for Telegram progress updates)
|
||||
telegram_chat_id: Chat ID to edit (for Telegram progress updates)
|
||||
|
||||
Returns:
|
||||
Dictionary with status of each channel
|
||||
"""
|
||||
results = {
|
||||
'email': False,
|
||||
'telegram': False
|
||||
}
|
||||
|
||||
account_count = len(accounts)
|
||||
logging.info(f"Sending notifications for {len(files)} files...")
|
||||
|
||||
# Send via email
|
||||
if self.config.EMAIL_ENABLED:
|
||||
results['email'] = self.email.send(files, account_count)
|
||||
|
||||
# Send via Telegram
|
||||
if self.config.TELEGRAM_ENABLED:
|
||||
results['telegram'] = self.telegram.send(
|
||||
files,
|
||||
accounts,
|
||||
telegram_message_id=telegram_message_id,
|
||||
telegram_chat_id=telegram_chat_id
|
||||
)
|
||||
|
||||
# Log summary
|
||||
success_count = sum(results.values())
|
||||
total_enabled = sum([self.config.EMAIL_ENABLED, self.config.TELEGRAM_ENABLED])
|
||||
|
||||
if success_count == total_enabled:
|
||||
logging.info(f"✓ All notifications sent successfully ({success_count}/{total_enabled})")
|
||||
elif success_count > 0:
|
||||
logging.warning(f"Partial notification success ({success_count}/{total_enabled})")
|
||||
else:
|
||||
logging.error("All notifications failed")
|
||||
|
||||
return results
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
playwright==1.48.0
|
||||
python-dotenv==1.0.0
|
||||
requests==2.31.0
|
||||
159
send_notifications.py
Normal file
159
send_notifications.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Script pentru trimiterea notificărilor cu fișiere existente
|
||||
Trimite ultimele fișiere CSV generate pe Email și Telegram
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from config import Config
|
||||
from notifications import NotificationService
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def find_latest_files(data_dir='./data', time_window_seconds=300):
|
||||
"""
|
||||
Găsește ultimele fișiere CSV generate
|
||||
|
||||
Args:
|
||||
data_dir: Directorul cu fișiere
|
||||
time_window_seconds: Intervalul de timp (în secunde) pentru a considera fișierele din aceeași sesiune
|
||||
|
||||
Returns:
|
||||
tuple: (solduri_csv_path, list_of_transaction_csvs)
|
||||
"""
|
||||
data_path = Path(data_dir)
|
||||
|
||||
if not data_path.exists():
|
||||
logging.error(f"Directorul {data_dir} nu există!")
|
||||
return None, []
|
||||
|
||||
# Găsește ultimul fișier solduri_*.csv
|
||||
solduri_files = sorted(data_path.glob('solduri_*.csv'), key=lambda x: x.stat().st_mtime, reverse=True)
|
||||
|
||||
if not solduri_files:
|
||||
logging.error("Nu s-a găsit niciun fișier solduri_*.csv!")
|
||||
return None, []
|
||||
|
||||
latest_solduri = solduri_files[0]
|
||||
solduri_time = latest_solduri.stat().st_mtime
|
||||
|
||||
logging.info(f"✓ Găsit fișier solduri: {latest_solduri.name}")
|
||||
logging.info(f" Timestamp: {datetime.fromtimestamp(solduri_time).strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# Găsește toate fișierele tranzactii_*.csv modificate în ultimele X secunde față de solduri
|
||||
all_transaction_files = list(data_path.glob('tranzactii_*.csv'))
|
||||
transaction_files = []
|
||||
|
||||
for tf in all_transaction_files:
|
||||
tf_time = tf.stat().st_mtime
|
||||
time_diff = abs(tf_time - solduri_time)
|
||||
|
||||
# Dacă fișierul tranzacții este din aceeași sesiune (în intervalul de timp)
|
||||
if time_diff <= time_window_seconds:
|
||||
transaction_files.append(tf)
|
||||
logging.info(f" ✓ {tf.name} (diferență: {int(time_diff)}s)")
|
||||
|
||||
# Sortează după timp
|
||||
transaction_files = sorted(transaction_files, key=lambda x: x.stat().st_mtime)
|
||||
|
||||
logging.info(f"✓ Găsite {len(transaction_files)} fișiere tranzacții din aceeași sesiune")
|
||||
|
||||
return latest_solduri, transaction_files
|
||||
|
||||
|
||||
def send_existing_files():
|
||||
"""Trimite fișierele existente pe Email și Telegram"""
|
||||
try:
|
||||
logging.info("=" * 60)
|
||||
logging.info("TRIMITERE NOTIFICĂRI CU FIȘIERE EXISTENTE")
|
||||
logging.info("=" * 60)
|
||||
|
||||
# Validează configurația
|
||||
Config.validate()
|
||||
|
||||
if not Config.ENABLE_NOTIFICATIONS:
|
||||
logging.error("ENABLE_NOTIFICATIONS=false în .env!")
|
||||
logging.error("Setează ENABLE_NOTIFICATIONS=true pentru a trimite notificări")
|
||||
return False
|
||||
|
||||
# Găsește ultimele fișiere
|
||||
solduri_csv, transaction_csvs = find_latest_files(Config.OUTPUT_DIR)
|
||||
|
||||
if not solduri_csv:
|
||||
logging.error("Nu există fișiere de trimis!")
|
||||
return False
|
||||
|
||||
# Pregătește lista de fișiere
|
||||
files_to_send = [str(solduri_csv)]
|
||||
for tf in transaction_csvs:
|
||||
files_to_send.append(str(tf))
|
||||
|
||||
logging.info("=" * 60)
|
||||
logging.info(f"Total fișiere de trimis: {len(files_to_send)}")
|
||||
logging.info("=" * 60)
|
||||
|
||||
# Estimează numărul de conturi din numărul de fișiere tranzacții
|
||||
account_count = len(transaction_csvs)
|
||||
|
||||
# Trimite notificările
|
||||
service = NotificationService(Config)
|
||||
results = service.send_all(files_to_send, account_count)
|
||||
|
||||
# Afișează rezumat
|
||||
logging.info("=" * 60)
|
||||
logging.info("REZUMAT NOTIFICĂRI")
|
||||
logging.info("=" * 60)
|
||||
|
||||
if Config.EMAIL_ENABLED:
|
||||
status = "✓ Succes" if results['email'] else "✗ Eșuat"
|
||||
logging.info(f"Email: {status}")
|
||||
else:
|
||||
logging.info("Email: Dezactivat")
|
||||
|
||||
if Config.TELEGRAM_ENABLED:
|
||||
status = "✓ Succes" if results['telegram'] else "✗ Eșuat"
|
||||
logging.info(f"Telegram: {status}")
|
||||
else:
|
||||
logging.info("Telegram: Dezactivat")
|
||||
|
||||
logging.info("=" * 60)
|
||||
|
||||
# Verifică dacă cel puțin un canal a avut succes
|
||||
success = any(results.values())
|
||||
|
||||
if success:
|
||||
logging.info("✓ Notificări trimise cu succes!")
|
||||
else:
|
||||
logging.warning("Notificările au eșuat. Verifică configurația în .env")
|
||||
|
||||
return success
|
||||
|
||||
except ValueError as e:
|
||||
logging.error(f"Eroare configurație: {e}")
|
||||
logging.error("Verifică setările din .env")
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare neașteptată: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.info("")
|
||||
logging.info("🔔 BTGO Scraper - Trimitere Notificări")
|
||||
logging.info("")
|
||||
|
||||
success = send_existing_files()
|
||||
|
||||
if success:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
276
telegram_trigger_bot.py
Normal file
276
telegram_trigger_bot.py
Normal file
@@ -0,0 +1,276 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Telegram Trigger Bot - Declanșează BTGO Scraper prin comandă Telegram
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import glob
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment
|
||||
load_dotenv()
|
||||
|
||||
# Configuration
|
||||
BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
|
||||
ALLOWED_USER_IDS = os.getenv('TELEGRAM_ALLOWED_USER_IDS', '').split(',') # Ex: "123456,789012"
|
||||
CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
|
||||
POLL_TIMEOUT = int(os.getenv('TELEGRAM_POLL_TIMEOUT', 60)) # Default 60 secunde
|
||||
|
||||
# Logging - force stdout instead of stderr (for Windows service logging)
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='[%(asctime)s] [%(levelname)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
stream=sys.stdout,
|
||||
force=True
|
||||
)
|
||||
|
||||
class TelegramTriggerBot:
|
||||
def __init__(self):
|
||||
self.bot_token = BOT_TOKEN
|
||||
self.allowed_users = [int(uid.strip()) for uid in ALLOWED_USER_IDS
|
||||
if uid.strip() and not uid.strip().startswith('#')]
|
||||
self.base_url = f"https://api.telegram.org/bot{self.bot_token}"
|
||||
self.last_update_id = 0
|
||||
self.poll_timeout = POLL_TIMEOUT
|
||||
|
||||
if not self.bot_token:
|
||||
raise ValueError("TELEGRAM_BOT_TOKEN nu este setat în .env!")
|
||||
|
||||
logging.info(f"Bot inițializat. Useri autorizați: {self.allowed_users}")
|
||||
logging.info(f"Long polling timeout: {self.poll_timeout}s")
|
||||
|
||||
# Înregistrare comenzi în meniul Telegram
|
||||
self._register_commands()
|
||||
|
||||
def _register_commands(self):
|
||||
"""Înregistrează comenzile bot în meniul Telegram (pentru DM și grupuri)"""
|
||||
try:
|
||||
url = f"{self.base_url}/setMyCommands"
|
||||
commands = [
|
||||
{"command": "scrape", "description": "Rulează scraper-ul BTGO"},
|
||||
{"command": "status", "description": "Status sistem"},
|
||||
{"command": "help", "description": "Ajutor comenzi"}
|
||||
]
|
||||
|
||||
response = requests.post(url, json={"commands": commands})
|
||||
if response.status_code == 200 and response.json().get('ok'):
|
||||
logging.info("✓ Comenzi înregistrate în meniul Telegram")
|
||||
else:
|
||||
logging.warning(f"Nu am putut înregistra comenzile: {response.text}")
|
||||
except Exception as e:
|
||||
logging.warning(f"Eroare înregistrare comenzi: {e}")
|
||||
|
||||
def send_message(self, chat_id, text, reply_to_message_id=None):
|
||||
"""Trimite mesaj text"""
|
||||
url = f"{self.base_url}/sendMessage"
|
||||
data = {
|
||||
'chat_id': chat_id,
|
||||
'text': text,
|
||||
'parse_mode': 'Markdown'
|
||||
}
|
||||
if reply_to_message_id:
|
||||
data['reply_to_message_id'] = reply_to_message_id
|
||||
response = requests.post(url, json=data)
|
||||
return response
|
||||
|
||||
def send_document(self, chat_id, file_path, caption=None):
|
||||
"""Trimite document (CSV/JSON)"""
|
||||
url = f"{self.base_url}/sendDocument"
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {'document': file}
|
||||
data = {'chat_id': chat_id}
|
||||
if caption:
|
||||
data['caption'] = caption
|
||||
response = requests.post(url, data=data, files=files)
|
||||
return response.json()
|
||||
|
||||
def is_user_allowed(self, user_id):
|
||||
"""Verifică dacă user-ul are permisiune"""
|
||||
if not self.allowed_users: # Dacă lista e goală, permite oricui
|
||||
return True
|
||||
return user_id in self.allowed_users
|
||||
|
||||
def run_scraper(self, chat_id, reply_to_message_id=None):
|
||||
"""Execută scraper-ul"""
|
||||
# Trimite mesaj inițial și salvează message_id pentru editare ulterioară
|
||||
response = self.send_message(chat_id, "*BTGO Scraper pornit*\n\nAsteapta 2FA pe telefon.", reply_to_message_id)
|
||||
message_id = None
|
||||
try:
|
||||
message_id = response.json()['result']['message_id']
|
||||
logging.info(f"Mesaj progress creat cu ID: {message_id}")
|
||||
except:
|
||||
logging.warning("Nu am putut salva message_id pentru progress updates")
|
||||
|
||||
try:
|
||||
# Rulează scraper-ul
|
||||
logging.info("Pornire scraper...")
|
||||
|
||||
# Prepare environment with global playwright path + Telegram progress info
|
||||
env = os.environ.copy()
|
||||
env['PLAYWRIGHT_BROWSERS_PATH'] = 'C:\\playwright-browsers'
|
||||
if message_id:
|
||||
env['TELEGRAM_CHAT_ID'] = str(chat_id)
|
||||
env['TELEGRAM_MESSAGE_ID'] = str(message_id)
|
||||
logging.info(f"Setting environment: TELEGRAM_CHAT_ID={chat_id}, TELEGRAM_MESSAGE_ID={message_id}")
|
||||
else:
|
||||
logging.warning("No message_id available for progress updates")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, 'btgo_scraper.py'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=600, # 10 minute timeout
|
||||
cwd=os.path.dirname(os.path.abspath(__file__)), # Run in bot's directory
|
||||
env=env # Pass environment with playwright path
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Succes - mesajul final va fi editat de notifications.py
|
||||
logging.info("Scraper finalizat cu succes")
|
||||
|
||||
else:
|
||||
# Eroare
|
||||
logging.error(f"Scraper eșuat cu cod {result.returncode}")
|
||||
error_msg = result.stderr[-1000:] if result.stderr else "Eroare necunoscută"
|
||||
self.send_message(
|
||||
chat_id,
|
||||
f"*EROARE SCRAPER*\n\n```\n{error_msg}\n```",
|
||||
reply_to_message_id
|
||||
)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logging.error("Timeout scraper")
|
||||
self.send_message(chat_id, "*TIMEOUT*\n\nScraper-ul a depasit 10 minute.", reply_to_message_id)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare execuție: {e}")
|
||||
self.send_message(chat_id, f"*EROARE EXECUTIE*\n\n```\n{str(e)}\n```", reply_to_message_id)
|
||||
|
||||
def handle_command(self, message):
|
||||
"""Procesează comenzi primite"""
|
||||
chat_id = message['chat']['id']
|
||||
chat_type = message['chat']['type'] # 'private', 'group', 'supergroup'
|
||||
chat_title = message['chat'].get('title', 'DM')
|
||||
user_id = message['from']['id']
|
||||
username = message['from'].get('username', 'Unknown')
|
||||
text = message.get('text', '')
|
||||
message_id = message.get('message_id')
|
||||
|
||||
# Normalizează comanda - elimină @username pentru grupuri (ex: /scrape@botname → /scrape)
|
||||
if '@' in text:
|
||||
text = text.split('@')[0]
|
||||
|
||||
# Log context
|
||||
context = f"grup '{chat_title}'" if chat_type in ['group', 'supergroup'] else "DM"
|
||||
logging.info(f"Mesaj de la {username} (ID: {user_id}) în {context}: {text}")
|
||||
|
||||
# Verifică autorizare
|
||||
if not self.is_user_allowed(user_id):
|
||||
logging.warning(f"User neautorizat: {user_id} în {context}")
|
||||
self.send_message(chat_id, "*ACCES INTERZIS*\n\nNu ai permisiunea sa folosesti acest bot.", message_id)
|
||||
return
|
||||
|
||||
# Procesează comenzi
|
||||
if text == '/start':
|
||||
welcome_msg = "*BTGO Scraper Trigger Bot*\n\n"
|
||||
if chat_type in ['group', 'supergroup']:
|
||||
welcome_msg += f"Bot activ in grupul *{chat_title}*\n\n"
|
||||
welcome_msg += (
|
||||
"Comenzi disponibile:\n"
|
||||
"`/scrape` - Ruleaza scraper-ul\n"
|
||||
"`/status` - Status sistem\n"
|
||||
"`/help` - Ajutor"
|
||||
)
|
||||
self.send_message(chat_id, welcome_msg, message_id)
|
||||
|
||||
elif text == '/scrape':
|
||||
logging.info(f"Comandă /scrape primită în {context}")
|
||||
self.run_scraper(chat_id, message_id)
|
||||
|
||||
elif text == '/status':
|
||||
data_dir = Path('data')
|
||||
csv_count = len(list(data_dir.glob('*.csv')))
|
||||
json_count = len(list(data_dir.glob('*.json')))
|
||||
|
||||
# Ultimul fișier
|
||||
all_files = sorted(data_dir.glob('solduri_*.csv'), key=os.path.getmtime, reverse=True)
|
||||
last_run = "N/A"
|
||||
if all_files:
|
||||
last_run = datetime.fromtimestamp(os.path.getmtime(all_files[0])).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
self.send_message(
|
||||
chat_id,
|
||||
f"*STATUS SISTEM*\n\n"
|
||||
f"Ultima rulare: `{last_run}`\n"
|
||||
f"Fisiere CSV: {csv_count}\n"
|
||||
f"Fisiere JSON: {json_count}\n"
|
||||
f"Working dir: `{os.getcwd()}`",
|
||||
message_id
|
||||
)
|
||||
|
||||
elif text == '/help':
|
||||
help_msg = "*GHID DE UTILIZARE*\n\n"
|
||||
if chat_type in ['group', 'supergroup']:
|
||||
help_msg += "IN GRUP: Toti membrii vad comenzile si rezultatele\n\n"
|
||||
help_msg += (
|
||||
"1. Trimite `/scrape` pentru a porni scraper-ul\n"
|
||||
"2. Asteapta notificarea de 2FA pe telefon\n"
|
||||
"3. Aproba in aplicatia George\n"
|
||||
"4. Primesti fisierele automat\n\n"
|
||||
"NOTE:\n"
|
||||
"- Scraper-ul ruleaza ~2-3 minute\n"
|
||||
"- Asigura-te ca VM-ul are browser vizibil"
|
||||
)
|
||||
self.send_message(chat_id, help_msg, message_id)
|
||||
|
||||
else:
|
||||
self.send_message(chat_id, f"*COMANDA NECUNOSCUTA*\n\n`{text}`\n\nFoloseste /help pentru comenzi.", message_id)
|
||||
|
||||
def get_updates(self):
|
||||
"""Preia update-uri de la Telegram"""
|
||||
url = f"{self.base_url}/getUpdates"
|
||||
params = {
|
||||
'offset': self.last_update_id + 1,
|
||||
'timeout': self.poll_timeout
|
||||
}
|
||||
response = requests.get(url, params=params, timeout=self.poll_timeout + 5)
|
||||
return response.json()
|
||||
|
||||
def run(self):
|
||||
"""Loop principal bot"""
|
||||
logging.info("Bot pornit. Așteaptă comenzi...")
|
||||
|
||||
while True:
|
||||
try:
|
||||
updates = self.get_updates()
|
||||
|
||||
if updates.get('ok'):
|
||||
for update in updates.get('result', []):
|
||||
self.last_update_id = update['update_id']
|
||||
|
||||
if 'message' in update:
|
||||
self.handle_command(update['message'])
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Bot oprit de utilizator")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare loop: {e}")
|
||||
import time
|
||||
time.sleep(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
bot = TelegramTriggerBot()
|
||||
bot.run()
|
||||
except Exception as e:
|
||||
logging.error(f"Eroare fatală: {e}")
|
||||
raise
|
||||
Reference in New Issue
Block a user