feat(telegram): bot bonuri fiscale — OCR → preview → Oracle write

- US-001: mută queue_client.py în data_entry/services/ocr/
- US-002/003/004: oracle_receipt_writer + oracle_server_id în DB
- US-005: receipt_handlers.py (PDF/photo/callback flow)
- US-006: wire handlers în main.py, per-schema connect, seq_cod.nextval
- US-007: .gitignore secrets/*.oracle_pass
- US-008/009/010: teste unit + integration + E2E
- setup-secrets.sh helper + template
- docs/telegram/README.md actualizat cu arhitectura nouă

Testat E2E pe DB live (MARIUSM_AUTO). COD din seq_cod.nextval.
pypdfium2 fallback pentru PDF decode (fără poppler).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 09:26:58 +00:00
parent 8234103884
commit e257fa5d5f
35 changed files with 4531 additions and 227 deletions

View File

@@ -6,6 +6,7 @@ using direct command handlers for financial data queries.
"""
import asyncio
import glob
import logging
import os
from pathlib import Path
@@ -68,6 +69,13 @@ from backend.modules.telegram.bot.handlers import (
# Import email authentication handler
from backend.modules.telegram.bot.email_handlers import email_login_handler
# Import receipt handlers (US-005: PDF/JPG OCR fiscal receipt flow)
from backend.modules.telegram.handlers.receipt_handlers import (
handle_document_message,
handle_photo_message,
handle_receipt_callback,
)
# Note: internal_api import removed - now served via main.py at /api/telegram/internal/*
# Configure logging
@@ -96,8 +104,14 @@ def create_telegram_application() -> Application:
"""
logger.info("Creating Telegram application...")
# Create application
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
# Create application with concurrent_updates so multiple users can use the bot
# in parallel (e.g. two users uploading receipts simultaneously).
application = (
Application.builder()
.token(TELEGRAM_BOT_TOKEN)
.concurrent_updates(True)
.build()
)
# Register email authentication conversation handler (must be before other handlers)
application.add_handler(email_login_handler)
@@ -140,6 +154,19 @@ def create_telegram_application() -> Application:
handle_text_message
))
# US-006: Receipt handlers (PDF/JPG fiscal receipt OCR flow)
# IMPORTANT: receipt CallbackQueryHandler must be registered BEFORE the
# catch-all button_callback so `receipt:*` callbacks are routed correctly.
application.add_handler(MessageHandler(
filters.Document.PDF | filters.Document.IMAGE,
handle_document_message
))
application.add_handler(MessageHandler(filters.PHOTO, handle_photo_message))
application.add_handler(CallbackQueryHandler(
handle_receipt_callback,
pattern=r'^receipt:'
))
# FAZA 4: Register callback query handler (for inline buttons)
application.add_handler(CallbackQueryHandler(button_callback))
@@ -156,12 +183,39 @@ def create_telegram_application() -> Application:
# ============================================================================
# Note: Internal API server removed - now served via main.py at /api/telegram/internal/*
def startup_cleanup() -> int:
"""
Remove orphan receipt temp files left over from a previous bot crash.
Receipt OCR flow writes downloaded files to `/tmp/receipt_*.*` and unlinks
them after confirm/cancel. If the bot died between download and cleanup,
those files remain on disk; we clean them on startup. Returns the count
of unlinked files (for logging).
"""
count = 0
for path_str in glob.glob('/tmp/receipt_*.*'):
try:
Path(path_str).unlink(missing_ok=True)
count += 1
except OSError as e:
logger.warning(f"Failed to unlink orphan receipt file {path_str}: {e}")
return count
async def startup():
"""
Initialize the bot application on startup.
"""
logger.info("🚀 ROA2WEB Telegram Bot - Starting up...")
# US-006: Sweep orphan receipt temp files from previous crashes
try:
orphans = startup_cleanup()
if orphans:
logger.info(f"🧹 Cleaned up {orphans} orphan receipt temp files")
except Exception as e:
logger.warning(f"⚠️ Receipt orphan cleanup failed (non-critical): {e}")
# Initialize database
try:
logger.info("Initializing SQLite database...")