From e2ec15939cc3f241b656483a762f16d4bd4396df Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Tue, 11 Nov 2025 01:03:02 +0200 Subject: [PATCH] Adauga comenzi Telegram pentru solduri si repara get_telegram_chat_id - Adauga /scrape_solduri - scraping rapid doar solduri (fara CSV tranzactii) - Adauga /solduri - afisare instant solduri din cache (fara 2FA) - Redenumeste comenzi pentru consistenta - Adauga suport BALANCES_ONLY in scraper (skip download tranzactii) - Repara get_telegram_chat_id.py - elimina input() interactiv - Imbunatateste output get_telegram_chat_id.py cu info bot si formatare Co-Authored-By: Claude --- btgo_scraper.py | 14 ++++- get_telegram_chat_id.py | 120 +++++++++++++++++++++++----------------- telegram_trigger_bot.py | 91 +++++++++++++++++++++++++++--- 3 files changed, 163 insertions(+), 62 deletions(-) diff --git a/btgo_scraper.py b/btgo_scraper.py index 90adcaa..dcc5098 100644 --- a/btgo_scraper.py +++ b/btgo_scraper.py @@ -93,8 +93,14 @@ class BTGoScraper: def run(self): """Entry point principal - orchestreaza tot flow-ul""" try: + # Check dacă rulăm în mod balances_only + balances_only = os.getenv('BALANCES_ONLY', 'false').lower() == 'true' + logging.info("=" * 60) - logging.info("Start BTGO Scraper") + if balances_only: + logging.info("Start BTGO Scraper (DOAR SOLDURI)") + else: + logging.info("Start BTGO Scraper") logging.info("=" * 60) with sync_playwright() as p: @@ -115,9 +121,11 @@ class BTGoScraper: accounts = self.read_accounts() csv_path, json_path = self.save_results(accounts) - # Descarcă tranzacții pentru toate conturile (optional) + # Descarcă tranzacții pentru toate conturile (doar dacă nu e balances_only) downloaded_files = [] - if self.config.DOWNLOAD_TRANSACTIONS: + if balances_only: + logging.info("Mod DOAR SOLDURI - skip download tranzactii") + elif self.config.DOWNLOAD_TRANSACTIONS: downloaded_files = self.download_transactions(accounts) else: logging.info("Download tranzacții dezactivat (DOWNLOAD_TRANSACTIONS=false)") diff --git a/get_telegram_chat_id.py b/get_telegram_chat_id.py index d352341..6330454 100644 --- a/get_telegram_chat_id.py +++ b/get_telegram_chat_id.py @@ -4,6 +4,7 @@ Helper script pentru obținerea Chat ID Telegram (pentru DM și grupuri) """ import os +import sys import requests from dotenv import load_dotenv @@ -13,11 +14,20 @@ load_dotenv() BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN') if not BOT_TOKEN: - print("❌ TELEGRAM_BOT_TOKEN nu este setat în .env!") + print("=" * 60) + print("EROARE: TELEGRAM_BOT_TOKEN nu este setat în .env") + print("=" * 60) print("\nAdaugă în .env:") print("TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrs") + print("=" * 60) exit(1) +def get_bot_info(): + """Preia informații despre bot""" + url = f"https://api.telegram.org/bot{BOT_TOKEN}/getMe" + response = requests.get(url) + return response.json() + def get_updates(): """Preia ultimele update-uri de la bot""" url = f"https://api.telegram.org/bot{BOT_TOKEN}/getUpdates" @@ -28,96 +38,102 @@ 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...") + # Verifică info bot + bot_data = get_bot_info() + if bot_data.get('ok'): + bot = bot_data['result'] + print(f"\nBot: @{bot.get('username')} ({bot.get('first_name')})") + print(f"Bot ID: {bot.get('id')}") + print(f"Status: ACTIV") + else: + print(f"\nERORE: Token invalid - {bot_data}") + exit(1) + + print("\n" + "=" * 60) + print("Căutare mesaje...") + print("=" * 60) + data = get_updates() if not data.get('ok'): - print(f"❌ Eroare API: {data}") + print(f"\nERORE 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") + print("\nNU S-AU GĂSIT MESAJE!") + print("\n" + "=" * 60) + print("INSTRUCȚIUNI:") + print("=" * 60) + print("1. Deschide Telegram") + print(f"2. Caută @{bot.get('username')} SAU deschide grupul cu bot-ul") + print("3. Trimite un mesaj (ex: /start sau /info)") + print("4. Rulează din nou acest script") + print("=" * 60) return - print(f"\n✅ Găsit {len(results)} mesaje!\n") + print(f"\nGăsit {len(results)} mesaje în total") + + # Procesează ultimele mesaje (ultimele 20) + seen_chats = {} + user_ids = set() + + print("\n" + "=" * 60) + print("CHAT IDs DETECTATE:") print("=" * 60) - # Procesează ultimele mesaje - seen_chats = {} - - for update in results: + for update in results[-20:]: # Ultimele 20 mesaje if 'message' in update: msg = update['message'] chat = msg['chat'] chat_id = chat['id'] chat_type = chat['type'] + user = msg['from'] + user_ids.add(user['id']) + text = msg.get('text', '(no text)') # Evită duplicate if chat_id in seen_chats: continue - seen_chats[chat_id] = True + seen_chats[chat_id] = chat # 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}") + print(f"\n[DM] {user.get('first_name', '')} {user.get('last_name', '')}") + print(f" Username: @{user.get('username', 'N/A')}") + print(f" User ID: {user['id']}") + 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(f"\n[GRUP] {chat.get('title', 'Unknown')}") + print(f" Chat ID: {chat_id}") + print(f" Tip: {chat_type}") + print(f" User: @{user.get('username', 'Unknown')} (ID: {user['id']})") - print() + print(f" Mesaj: \"{text[:60]}\"") + print("\n" + "=" * 60) + print("CONFIGURARE .env:") print("=" * 60) - print("\n💡 Pentru configurare .env:") - print() # Recomandări - for chat_id, chat_data in seen_chats.items(): + for chat_id, chat in seen_chats.items(): if chat_id < 0: # Grup - print(f"# Pentru grup (notificări + comenzi):") + print(f"\n# Grup: {chat.get('title', 'Unknown')}") print(f"TELEGRAM_CHAT_ID={chat_id}") else: # DM - print(f"# Pentru DM:") + print(f"\n# DM") print(f"TELEGRAM_CHAT_ID={chat_id}") - print() - print("# User IDs autorizați (pot rula /scrape):") - print("TELEGRAM_ALLOWED_USER_IDS=", end="") + if user_ids: + user_ids_str = ",".join(str(uid) for uid in sorted(user_ids)) + print(f"\n# User IDs autorizați") + print(f"TELEGRAM_ALLOWED_USER_IDS={user_ids_str}") - # 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) + print("\n" + "=" * 60) if __name__ == "__main__": main() diff --git a/telegram_trigger_bot.py b/telegram_trigger_bot.py index 93e4afa..619eb72 100644 --- a/telegram_trigger_bot.py +++ b/telegram_trigger_bot.py @@ -9,6 +9,7 @@ import io import subprocess import logging import json +import csv import zipfile from pathlib import Path from datetime import datetime @@ -65,6 +66,8 @@ class TelegramTriggerBot: commands = [ {"command": "scrape", "description": "Rulează scraper-ul BTGO"}, {"command": "scrape_zip", "description": "Rulează scraper + trimite ZIP"}, + {"command": "scrape_solduri", "description": "Extrage doar soldurile (fără CSV)"}, + {"command": "solduri", "description": "Afișează ultimul fișier solduri"}, {"command": "zip", "description": "Trimite ultimele fișiere ca ZIP"}, {"command": "status", "description": "Status sistem"}, {"command": "help", "description": "Ajutor comenzi"} @@ -108,11 +111,12 @@ class TelegramTriggerBot: return True return user_id in self.allowed_users - def run_scraper(self, chat_id, reply_to_message_id=None, send_as_zip=False): + def run_scraper(self, chat_id, reply_to_message_id=None, send_as_zip=False, balances_only=False): """Execută scraper-ul""" # Trimite mesaj inițial și salvează message_id pentru editare ulterioară zip_msg = " (arhiva ZIP)" if send_as_zip else "" - response = self.send_message(chat_id, f"*BTGO Scraper pornit{zip_msg}*\n\nAsteapta 2FA pe telefon.", reply_to_message_id) + balances_msg = " - DOAR SOLDURI" if balances_only else "" + response = self.send_message(chat_id, f"*BTGO Scraper pornit{zip_msg}{balances_msg}*\n\nAsteapta 2FA pe telefon.", reply_to_message_id) message_id = None try: message_id = response.json()['result']['message_id'] @@ -122,7 +126,7 @@ class TelegramTriggerBot: try: # Rulează scraper-ul - logging.info(f"Pornire scraper (send_as_zip={send_as_zip})...") + logging.info(f"Pornire scraper (send_as_zip={send_as_zip}, balances_only={balances_only})...") # Prepare environment with global playwright path + Telegram progress info env = os.environ.copy() @@ -138,6 +142,11 @@ class TelegramTriggerBot: if send_as_zip: env['SEND_AS_ZIP'] = 'true' logging.info("Mod ZIP activat - va trimite arhivă ZIP") + + # Dacă balances_only, comunică să nu descarce tranzacții + if balances_only: + env['BALANCES_ONLY'] = 'true' + logging.info("Mod DOAR SOLDURI activat - nu va descărca tranzacții") else: logging.warning("No message_id available for progress updates") @@ -172,6 +181,59 @@ class TelegramTriggerBot: 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 show_cached_balances(self, chat_id, reply_to_message_id=None): + """Afișează soldurile din cel mai recent fișier solduri.csv""" + try: + data_dir = Path('data') + + if not data_dir.exists(): + self.send_message(chat_id, "*EROARE*\n\nDirectorul 'data' nu există!", reply_to_message_id) + return + + # Găsește ultimul fișier solduri + solduri_files = sorted(data_dir.glob('solduri_*.csv'), key=lambda x: x.stat().st_mtime, reverse=True) + + if not solduri_files: + self.send_message(chat_id, "*EROARE*\n\nNu s-au găsit fișiere solduri!", reply_to_message_id) + return + + latest_solduri = solduri_files[0] + solduri_time = latest_solduri.stat().st_mtime + file_datetime = datetime.fromtimestamp(solduri_time).strftime('%Y-%m-%d %H:%M:%S') + + # Citește fișierul CSV + accounts = [] + with open(latest_solduri, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + accounts.append({ + 'nume_cont': row['nume_cont'], + 'sold': float(row['sold']), + 'moneda': row['moneda'] + }) + + # Construiește mesaj cu solduri + total_ron = sum(acc['sold'] for acc in accounts if acc.get('moneda') == 'RON') + + message = f"*SOLDURI BANCARE*\n\n" + message += f"Data: {file_datetime}\n" + message += f"Conturi: {len(accounts)}\n\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*TOTAL: {total_ron:,.2f} RON*" + + self.send_message(chat_id, message, reply_to_message_id) + logging.info(f"Afișat solduri cached din {latest_solduri.name}") + + except Exception as e: + logging.error(f"Eroare show_cached_balances: {e}", exc_info=True) + self.send_message(chat_id, f"*EROARE*\n\n```\n{str(e)}\n```", reply_to_message_id) + def send_zip_files(self, chat_id, reply_to_message_id=None): """Trimite ultimele fișiere ca arhivă ZIP""" try: @@ -332,8 +394,10 @@ class TelegramTriggerBot: welcome_msg += f"Bot activ in grupul *{chat_title}*\n\n" welcome_msg += ( "Comenzi disponibile:\n" - "`/scrape` - Ruleaza scraper-ul\n" + "`/scrape` - Ruleaza scraper-ul complet\n" "`/scrape_zip` - Ruleaza scraper + trimite ZIP\n" + "`/scrape_solduri` - Extrage doar soldurile (rapid)\n" + "`/solduri` - Afiseaza ultimul fisier solduri\n" "`/zip` - Trimite ultimele fisiere ca ZIP\n" "`/status` - Status sistem\n" "`/help` - Ajutor" @@ -348,6 +412,14 @@ class TelegramTriggerBot: logging.info(f"Comandă /scrape_zip primită în {context}") self.run_scraper(chat_id, message_id, send_as_zip=True) + elif text == '/scrape_solduri': + logging.info(f"Comandă /scrape_solduri primită în {context}") + self.run_scraper(chat_id, message_id, balances_only=True) + + elif text == '/solduri': + logging.info(f"Comandă /solduri primită în {context}") + self.show_cached_balances(chat_id, message_id) + elif text == '/zip': logging.info(f"Comandă /zip primită în {context}") self.send_zip_files(chat_id, message_id) @@ -381,20 +453,25 @@ class TelegramTriggerBot: "*COMENZI:*\n" "`/scrape` - Ruleaza scraper + trimite fisiere individuale\n" "`/scrape_zip` - Ruleaza scraper + trimite arhiva ZIP\n" + "`/scrape_solduri` - Extrage doar soldurile (fara CSV tranzactii)\n" + "`/solduri` - Afiseaza ultimul fisier solduri (instant)\n" "`/zip` - Trimite ultimele fisiere ca arhiva ZIP (fara scraping)\n" "`/status` - Informatii sistem\n" "`/help` - Acest mesaj\n\n" "*GHID SCRAPER:*\n" - "1. Trimite `/scrape` sau `/scrape_zip`\n" + "1. Trimite `/scrape`, `/scrape_zip` sau `/scrape_solduri`\n" "2. Asteapta notificarea de 2FA pe telefon\n" "3. Aproba in aplicatia George\n" "4. Primesti fisierele automat\n\n" "*DIFERENTE:*\n" "• `/scrape` - Fisiere individuale (CSV + JSON)\n" "• `/scrape_zip` - Un singur ZIP cu toate fisierele\n" - "• `/zip` - Rapid, foloseste datele existente\n\n" + "• `/scrape_solduri` - Doar solduri (RAPID - fara CSV tranzactii)\n" + "• `/solduri` - Vizualizare rapida (fara 2FA)\n" + "• `/zip` - Fisiere existente (fara scraping)\n\n" "*NOTE:*\n" - "- Scraper-ul ruleaza ~2-3 minute\n" + "- Scraper complet: ~2-3 minute\n" + "- Scraper solduri: ~30-40 secunde\n" "- VM-ul trebuie sa aiba browser vizibil" ) self.send_message(chat_id, help_msg, message_id)