Refactor Telegram bot login flow: clean UI with single editable message
- Consolidate login flow to use single message that edits in place - Auto-delete all user messages (email, code, password, /start command) - Remove all emojis from bot messages for cleaner interface - Fix "Retrimite Cod" bug - buttons now persist after resending code - Replace "Oracle" with "ROA" in all user-facing messages - Add clear instructions for each step (email input, code input, password) - Implement message tracking with context.user_data['login_message_id'] - Clean chat history - only menu message remains visible Files modified: - app/bot/email_handlers.py: Complete refactor of email login flow - app/bot/handlers.py: Update /start command to delete user message and edit existing message 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ from telegram.ext import (
|
|||||||
)
|
)
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from app.auth.email_auth import (
|
from app.auth.email_auth import (
|
||||||
is_valid_email_format,
|
is_valid_email_format,
|
||||||
@@ -44,6 +45,67 @@ AWAITING_EMAIL, AWAITING_CODE, AWAITING_PASSWORD = range(3)
|
|||||||
MAX_CODE_ATTEMPTS = 3
|
MAX_CODE_ATTEMPTS = 3
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
async def edit_login_message(
|
||||||
|
context: ContextTypes.DEFAULT_TYPE,
|
||||||
|
chat_id: int,
|
||||||
|
text: str,
|
||||||
|
reply_markup=None,
|
||||||
|
parse_mode="Markdown"
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Helper function to edit the login message stored in context.
|
||||||
|
If message_id is not stored, creates a new message instead.
|
||||||
|
"""
|
||||||
|
message_id = context.user_data.get('login_message_id')
|
||||||
|
|
||||||
|
if message_id:
|
||||||
|
try:
|
||||||
|
await context.bot.edit_message_text(
|
||||||
|
chat_id=chat_id,
|
||||||
|
message_id=message_id,
|
||||||
|
text=text,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=parse_mode
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not edit message {message_id}: {e}")
|
||||||
|
# Fallback: send new message and update ID
|
||||||
|
msg = await context.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
text=text,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=parse_mode
|
||||||
|
)
|
||||||
|
context.user_data['login_message_id'] = msg.message_id
|
||||||
|
else:
|
||||||
|
# No message ID stored - create new message
|
||||||
|
msg = await context.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
text=text,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=parse_mode
|
||||||
|
)
|
||||||
|
context.user_data['login_message_id'] = msg.message_id
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_login_message(context: ContextTypes.DEFAULT_TYPE, chat_id: int):
|
||||||
|
"""Delete the login message and clear the message_id from context"""
|
||||||
|
message_id = context.user_data.get('login_message_id')
|
||||||
|
|
||||||
|
if message_id:
|
||||||
|
try:
|
||||||
|
await context.bot.delete_message(chat_id=chat_id, message_id=message_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not delete message {message_id}: {e}")
|
||||||
|
|
||||||
|
# Clear from context
|
||||||
|
context.user_data.pop('login_message_id', None)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# ENTRY POINTS: /login command and action:login button
|
# ENTRY POINTS: /login command and action:login button
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -82,20 +144,16 @@ async def login_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
await update.message.reply_text(
|
# CREATE message and SAVE message_id
|
||||||
"**Alege metoda de autentificare:**\n\n"
|
msg = await update.message.reply_text(
|
||||||
"**Email + Parolă (2FA)**\n"
|
"Alege metoda de autentificare:",
|
||||||
" • Primești cod pe email\n"
|
|
||||||
" • Introduci codul\n"
|
|
||||||
" • Introduci parola Oracle\n\n"
|
|
||||||
"**Web App**\n"
|
|
||||||
" • Login în aplicația web\n"
|
|
||||||
" • Generează cod de linking\n"
|
|
||||||
" • Trimite codul cu /start",
|
|
||||||
reply_markup=reply_markup,
|
reply_markup=reply_markup,
|
||||||
parse_mode="Markdown"
|
parse_mode="Markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Save message ID for future edits
|
||||||
|
context.user_data['login_message_id'] = msg.message_id
|
||||||
|
|
||||||
return AWAITING_EMAIL
|
return AWAITING_EMAIL
|
||||||
|
|
||||||
|
|
||||||
@@ -138,20 +196,16 @@ async def action_login_callback(update: Update, context: ContextTypes.DEFAULT_TY
|
|||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
# EDIT existing menu message and SAVE message_id
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"**Alege metoda de autentificare:**\n\n"
|
"Alege metoda de autentificare:",
|
||||||
"**Email + Parolă (2FA)**\n"
|
|
||||||
" • Primești cod pe email\n"
|
|
||||||
" • Introduci codul\n"
|
|
||||||
" • Introduci parola Oracle\n\n"
|
|
||||||
"**Web App**\n"
|
|
||||||
" • Login în aplicația web\n"
|
|
||||||
" • Generează cod de linking\n"
|
|
||||||
" • Trimite codul cu /start",
|
|
||||||
reply_markup=reply_markup,
|
reply_markup=reply_markup,
|
||||||
parse_mode="Markdown"
|
parse_mode="Markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Save message ID for future edits
|
||||||
|
context.user_data['login_message_id'] = query.message.message_id
|
||||||
|
|
||||||
return AWAITING_EMAIL
|
return AWAITING_EMAIL
|
||||||
|
|
||||||
|
|
||||||
@@ -168,12 +222,12 @@ async def email_login_callback(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
|
# IMPORTANT: Salvează message_id înainte de a edita
|
||||||
|
context.user_data['login_message_id'] = query.message.message_id
|
||||||
|
|
||||||
|
# EDIT same message - remove buttons, ask for email
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"**Autentificare prin Email + Parolă**\n\n"
|
text="Introdu adresa de email ROA:",
|
||||||
"Te rugăm să introduci adresa ta de **email Oracle**:\n\n"
|
|
||||||
"Exemplu: nume.prenume@companie.ro\n\n"
|
|
||||||
"Vei primi un cod de 6 cifre pe email.\n\n"
|
|
||||||
"Scrie /cancel pentru a anula.",
|
|
||||||
parse_mode="Markdown"
|
parse_mode="Markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -197,6 +251,9 @@ async def web_login_info_callback(update: Update, context: ContextTypes.DEFAULT_
|
|||||||
parse_mode="Markdown"
|
parse_mode="Markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# IMPORTANT: Salvează message_id pentru ca /start să poată edita același mesaj
|
||||||
|
context.user_data['web_login_message_id'] = query.message.message_id
|
||||||
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
@@ -209,13 +266,22 @@ async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
email = update.message.text.strip().lower()
|
email = update.message.text.strip().lower()
|
||||||
user_id = update.effective_user.id
|
user_id = update.effective_user.id
|
||||||
|
|
||||||
|
# ȘTERG mesajul utilizatorului imediat (chat curat)
|
||||||
|
try:
|
||||||
|
await update.message.delete()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not delete email message: {e}")
|
||||||
|
|
||||||
# Validare format email
|
# Validare format email
|
||||||
if not is_valid_email_format(email):
|
if not is_valid_email_format(email):
|
||||||
await update.message.reply_text(
|
# Show error in main message
|
||||||
"**Email invalid**\n\n"
|
await edit_login_message(
|
||||||
"Te rugăm să introduci o adresă de email validă.\n\n"
|
context=context,
|
||||||
"Format: nume@domeniu.ro",
|
chat_id=update.effective_chat.id,
|
||||||
parse_mode="Markdown"
|
text="Email invalid\n\nIntrodu o adresă validă (nume@domeniu.ro)",
|
||||||
|
reply_markup=InlineKeyboardMarkup([
|
||||||
|
[InlineKeyboardButton("Anulează", callback_data="cancel")]
|
||||||
|
])
|
||||||
)
|
)
|
||||||
return AWAITING_EMAIL
|
return AWAITING_EMAIL
|
||||||
|
|
||||||
@@ -226,8 +292,13 @@ async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
await delete_user_email_codes(user_id)
|
await delete_user_email_codes(user_id)
|
||||||
logger.info(f"Deleted existing pending code for user {user_id}")
|
logger.info(f"Deleted existing pending code for user {user_id}")
|
||||||
|
|
||||||
# Loading message
|
# EDIT login message to show loading
|
||||||
loading_msg = await update.message.reply_text("Verificare email...")
|
await edit_login_message(
|
||||||
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Verificare email...",
|
||||||
|
reply_markup=None
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Verifică email în Oracle
|
# Verifică email în Oracle
|
||||||
@@ -250,8 +321,10 @@ async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not code_saved:
|
if not code_saved:
|
||||||
await loading_msg.edit_text(
|
await edit_login_message(
|
||||||
"Eroare la salvarea codului. Te rugăm să încerci din nou cu /login"
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare la salvarea codului.\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -263,16 +336,15 @@ async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
logger.error(f"Failed to send email to {email}")
|
logger.error(f"Failed to send email to {email}")
|
||||||
# Don't reveal this to user - they'll timeout naturally
|
# Don't reveal this to user - they'll timeout naturally
|
||||||
|
|
||||||
|
# Wait 1 second for better UX (looks like verification happened)
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
# ALWAYS show this message (prevent enumeration)
|
# ALWAYS show this message (prevent enumeration)
|
||||||
await loading_msg.edit_text(
|
# EDIT same message with success + buttons
|
||||||
"**Cod trimis**\n\n"
|
await edit_login_message(
|
||||||
f"Am trimis un cod de 6 cifre pe **{email}**\n\n"
|
context=context,
|
||||||
"Verifică:\n"
|
chat_id=update.effective_chat.id,
|
||||||
" • Inbox-ul\n"
|
text=f"Cod trimis pe {email}\n\nIntrodu codul primit pe email:",
|
||||||
" • Folderul Spam/Junk\n\n"
|
|
||||||
"Codul expiră în **5 minute**\n\n"
|
|
||||||
"Introdu codul aici sau apasă butonul de mai jos.",
|
|
||||||
parse_mode="Markdown",
|
|
||||||
reply_markup=InlineKeyboardMarkup([
|
reply_markup=InlineKeyboardMarkup([
|
||||||
[InlineKeyboardButton("Retrimite Cod", callback_data=f"resend:{email}")],
|
[InlineKeyboardButton("Retrimite Cod", callback_data=f"resend:{email}")],
|
||||||
[InlineKeyboardButton("Anulează", callback_data="cancel")]
|
[InlineKeyboardButton("Anulează", callback_data="cancel")]
|
||||||
@@ -287,8 +359,10 @@ async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in receive_email: {e}", exc_info=True)
|
logger.error(f"Error in receive_email: {e}", exc_info=True)
|
||||||
await loading_msg.edit_text(
|
await edit_login_message(
|
||||||
"Eroare internă. Te rugăm să încerci din nou mai târziu."
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare internă.\n\nIncearcă din nou mai târziu."
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -302,13 +376,22 @@ async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
code = update.message.text.strip()
|
code = update.message.text.strip()
|
||||||
user_id = update.effective_user.id
|
user_id = update.effective_user.id
|
||||||
|
|
||||||
|
# ȘTERG mesajul utilizatorului imediat (chat curat)
|
||||||
|
try:
|
||||||
|
await update.message.delete()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not delete code message: {e}")
|
||||||
|
|
||||||
# Validare format cod (6 digits)
|
# Validare format cod (6 digits)
|
||||||
if not (code.isdigit() and len(code) == 6):
|
if not (code.isdigit() and len(code) == 6):
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"**Cod invalid**\n\n"
|
context=context,
|
||||||
"Te rugăm să introduci cele **6 cifre** din email.\n\n"
|
chat_id=update.effective_chat.id,
|
||||||
"Format: 123456",
|
text="Cod invalid\n\nIntrodu cele 6 cifre din email.",
|
||||||
parse_mode="Markdown"
|
reply_markup=InlineKeyboardMarkup([
|
||||||
|
[InlineKeyboardButton("Retrimite Cod", callback_data=f"resend:{context.user_data.get('pending_email', '')}")],
|
||||||
|
[InlineKeyboardButton("Anulează", callback_data="cancel")]
|
||||||
|
])
|
||||||
)
|
)
|
||||||
return AWAITING_CODE
|
return AWAITING_CODE
|
||||||
|
|
||||||
@@ -317,11 +400,11 @@ async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
code_data = await get_email_auth_code(code)
|
code_data = await get_email_auth_code(code)
|
||||||
|
|
||||||
if not code_data:
|
if not code_data:
|
||||||
await update.message.reply_text(
|
# EDIT login message to show error
|
||||||
"**Cod invalid sau expirat**\n\n"
|
await edit_login_message(
|
||||||
"Te rugăm să:\n"
|
context=context,
|
||||||
"• Verifici codul din email\n"
|
chat_id=update.effective_chat.id,
|
||||||
"• Sau reîncepi cu /login"
|
text="Cod invalid sau expirat\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -329,19 +412,19 @@ async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
# 1. Check if already used
|
# 1. Check if already used
|
||||||
if code_data['used']:
|
if code_data['used']:
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"**Cod deja folosit**\n\n"
|
context=context,
|
||||||
"Fiecare cod poate fi folosit o singură dată.\n\n"
|
chat_id=update.effective_chat.id,
|
||||||
"Te rugăm să reîncepi cu /login"
|
text="Cod deja folosit\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
# 2. Check if expired
|
# 2. Check if expired
|
||||||
if datetime.now() > code_data['expires_at']:
|
if datetime.now() > code_data['expires_at']:
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"**Cod expirat**\n\n"
|
context=context,
|
||||||
"Codul era valabil 5 minute.\n\n"
|
chat_id=update.effective_chat.id,
|
||||||
"Te rugăm să reîncepi cu /login"
|
text="Cod expirat\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -351,16 +434,19 @@ async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
f"User {user_id} tried to use code belonging to "
|
f"User {user_id} tried to use code belonging to "
|
||||||
f"user {code_data['telegram_user_id']}"
|
f"user {code_data['telegram_user_id']}"
|
||||||
)
|
)
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"**Cod invalid**"
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Cod invalid"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
# 4. Check failed attempts (max 3)
|
# 4. Check failed attempts (max 3)
|
||||||
if code_data['failed_attempts'] >= MAX_CODE_ATTEMPTS:
|
if code_data['failed_attempts'] >= MAX_CODE_ATTEMPTS:
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"**Prea multe încercări greșite**\n\n"
|
context=context,
|
||||||
"Te rugăm să reîncepi cu /login pentru un cod nou."
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Prea multe încercări greșite\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -375,23 +461,22 @@ async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
code_data['email']
|
code_data['email']
|
||||||
)
|
)
|
||||||
|
|
||||||
await update.message.reply_text(
|
# EDIT same message - ask for password
|
||||||
"**Cod validat cu succes**\n\n"
|
await edit_login_message(
|
||||||
"Acum introdu **parola ta Oracle**:\n\n"
|
context=context,
|
||||||
"**Important:**\n"
|
chat_id=update.effective_chat.id,
|
||||||
" • Parola va fi ștearsă automat\n"
|
text="Cod validat!\n\nIntroduci parola ROA:",
|
||||||
" • Nu va fi vizibilă în chat\n"
|
reply_markup=None # No buttons for security
|
||||||
" • Verificată direct în Oracle\n\n"
|
|
||||||
"Scrie /cancel pentru a anula.",
|
|
||||||
parse_mode="Markdown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return AWAITING_PASSWORD
|
return AWAITING_PASSWORD
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error validating code: {e}", exc_info=True)
|
logger.error(f"Error validating code: {e}", exc_info=True)
|
||||||
await update.message.reply_text(
|
await edit_login_message(
|
||||||
"Eroare la validarea codului. Te rugăm să încerci din nou."
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare la validarea codului.\n\nIncearcă din nou."
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -404,7 +489,11 @@ async def resend_code_callback(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
# Extract email from callback data
|
# Extract email from callback data
|
||||||
callback_data = query.data # Format: "resend:email@example.com"
|
callback_data = query.data # Format: "resend:email@example.com"
|
||||||
if not callback_data.startswith("resend:"):
|
if not callback_data.startswith("resend:"):
|
||||||
await query.edit_message_text("Eroare. Te rugăm să reîncepi cu /login")
|
await edit_login_message(
|
||||||
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare\n\nIncearcă din nou cu /login"
|
||||||
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
email = callback_data.split(":", 1)[1]
|
email = callback_data.split(":", 1)[1]
|
||||||
@@ -412,9 +501,10 @@ async def resend_code_callback(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
|
|
||||||
# Check rate limiting for resend (max 2 per 10 minutes)
|
# Check rate limiting for resend (max 2 per 10 minutes)
|
||||||
if not await check_rate_limit(f"resend_{user_id}", max_attempts=2, window_minutes=10):
|
if not await check_rate_limit(f"resend_{user_id}", max_attempts=2, window_minutes=10):
|
||||||
await query.edit_message_text(
|
await edit_login_message(
|
||||||
"Prea multe solicitări de retrimitere.\n\n"
|
context=context,
|
||||||
"Te rugăm să aștepți 10 minute."
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Prea multe solicitări\n\nAșteaptă 10 minute."
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -424,8 +514,10 @@ async def resend_code_callback(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
if not username:
|
if not username:
|
||||||
username = await verify_email_in_oracle(email)
|
username = await verify_email_in_oracle(email)
|
||||||
if not username:
|
if not username:
|
||||||
await query.edit_message_text(
|
await edit_login_message(
|
||||||
"Eroare. Te rugăm să reîncepi cu /login"
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -447,11 +539,15 @@ async def resend_code_callback(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
email_service = get_email_service()
|
email_service = get_email_service()
|
||||||
await email_service.send_auth_code(email, code, username)
|
await email_service.send_auth_code(email, code, username)
|
||||||
|
|
||||||
await query.edit_message_text(
|
# FIX BUG: EDIT message and KEEP buttons!
|
||||||
f"**Cod retrimis pe {email}**\n\n"
|
await edit_login_message(
|
||||||
"Verifică inbox-ul (și spam).\n\n"
|
context=context,
|
||||||
"Introdu codul aici.",
|
chat_id=update.effective_chat.id,
|
||||||
parse_mode="Markdown"
|
text=f"Cod retrimis pe {email}\n\nIntrodu codul primit pe email:",
|
||||||
|
reply_markup=InlineKeyboardMarkup([
|
||||||
|
[InlineKeyboardButton("Retrimite Cod", callback_data=f"resend:{email}")],
|
||||||
|
[InlineKeyboardButton("Anulează", callback_data="cancel")]
|
||||||
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
return AWAITING_CODE
|
return AWAITING_CODE
|
||||||
@@ -479,14 +575,19 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
session_token = context.user_data.get('session_token')
|
session_token = context.user_data.get('session_token')
|
||||||
|
|
||||||
if not all([username, email, session_token]):
|
if not all([username, email, session_token]):
|
||||||
await update.effective_chat.send_message(
|
await edit_login_message(
|
||||||
"Sesiune expirată. Te rugăm să reîncepi cu /login"
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Sesiune expirată\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
# Loading message
|
# EDIT login message to show loading
|
||||||
loading_msg = await update.effective_chat.send_message(
|
await edit_login_message(
|
||||||
"Verificare credențiale în Oracle..."
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Verificare...",
|
||||||
|
reply_markup=None
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -501,10 +602,10 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not response.get('success'):
|
if not response.get('success'):
|
||||||
await loading_msg.edit_text(
|
await edit_login_message(
|
||||||
"**Credențiale invalide**\n\n"
|
context=context,
|
||||||
"Parolă incorectă sau cont inactiv.\n\n"
|
chat_id=update.effective_chat.id,
|
||||||
"Te rugăm să reîncepi cu /login"
|
text="Credențiale invalide\n\nParolă incorectă sau cont inactiv.\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -533,17 +634,10 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
clear_rate_limit(f"login_{user_id}")
|
clear_rate_limit(f"login_{user_id}")
|
||||||
clear_rate_limit(f"resend_{user_id}")
|
clear_rate_limit(f"resend_{user_id}")
|
||||||
|
|
||||||
# Delete loading message
|
# Get session and active company BEFORE editing message
|
||||||
try:
|
|
||||||
await loading_msg.delete()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Show main menu with buttons (user is now authenticated)
|
|
||||||
from app.agent.session import get_session_manager
|
from app.agent.session import get_session_manager
|
||||||
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
||||||
|
|
||||||
# Get session and active company
|
|
||||||
session_manager = get_session_manager()
|
session_manager = get_session_manager()
|
||||||
session = await session_manager.get_or_create_session(user_id)
|
session = await session_manager.get_or_create_session(user_id)
|
||||||
company = session.get_active_company()
|
company = session.get_active_company()
|
||||||
@@ -559,28 +653,22 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
cache_enabled=True # Default enabled
|
cache_enabled=True # Default enabled
|
||||||
)
|
)
|
||||||
|
|
||||||
# Success message with company info
|
# Menu message with company info
|
||||||
companies_count = len(response.get('companies', []))
|
companies_count = len(response.get('companies', []))
|
||||||
|
|
||||||
if company_name:
|
if company_name:
|
||||||
welcome_message = pad_message_for_wide_buttons(
|
menu_text = f"{company_name}"
|
||||||
f"**Autentificat cu succes**\n\n"
|
|
||||||
f"Bun venit, **{response['username']}**\n\n"
|
|
||||||
f"{company_name}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
welcome_message = pad_message_for_wide_buttons(
|
menu_text = f"Companii disponibile: {companies_count}\n\nSelectează o companie pentru a continua"
|
||||||
f"**Autentificat cu succes**\n\n"
|
|
||||||
f"Bun venit, **{response['username']}**\n\n"
|
|
||||||
f"Companii disponibile: **{companies_count}**\n\n"
|
|
||||||
f"Selectează o companie pentru a continua"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Send menu with buttons
|
menu_message = pad_message_for_wide_buttons(menu_text)
|
||||||
await update.effective_chat.send_message(
|
|
||||||
welcome_message,
|
# EDIT login message to show menu (no deletion, direct edit)
|
||||||
reply_markup=keyboard,
|
await edit_login_message(
|
||||||
parse_mode="Markdown"
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text=menu_message,
|
||||||
|
reply_markup=keyboard
|
||||||
)
|
)
|
||||||
|
|
||||||
# Clear sensitive data from context
|
# Clear sensitive data from context
|
||||||
@@ -592,9 +680,10 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during password verification: {e}", exc_info=True)
|
logger.error(f"Error during password verification: {e}", exc_info=True)
|
||||||
await loading_msg.edit_text(
|
await edit_login_message(
|
||||||
"Eroare la autentificare.\n\n"
|
context=context,
|
||||||
"Te rugăm să încerci din nou cu /login"
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Eroare la autentificare.\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -605,32 +694,44 @@ async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
async def cancel_login(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def cancel_login(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Cancel conversation"""
|
"""Cancel conversation"""
|
||||||
context.user_data.clear()
|
|
||||||
|
|
||||||
if update.message:
|
# EDIT login message to show cancellation (don't delete)
|
||||||
await update.message.reply_text(
|
if update.callback_query:
|
||||||
"Autentificare anulată.\n\n"
|
# Called from button
|
||||||
"Folosește /login pentru a încerca din nou."
|
await edit_login_message(
|
||||||
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Login anulat",
|
||||||
|
reply_markup=None
|
||||||
)
|
)
|
||||||
elif update.callback_query:
|
elif update.message:
|
||||||
await update.callback_query.edit_message_text(
|
# Called from /cancel command
|
||||||
"Autentificare anulată."
|
await edit_login_message(
|
||||||
|
context=context,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
text="Login anulat",
|
||||||
|
reply_markup=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Clear context
|
||||||
|
context.user_data.clear()
|
||||||
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
async def conversation_timeout(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def conversation_timeout(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Handler for conversation timeout"""
|
"""Handler for conversation timeout"""
|
||||||
context.user_data.clear()
|
|
||||||
|
|
||||||
await update.effective_chat.send_message(
|
# EDIT login message to show timeout
|
||||||
"**Sesiune expirată**\n\n"
|
await edit_login_message(
|
||||||
"Conversația de autentificare a expirat după 5 minute de inactivitate.\n\n"
|
context=context,
|
||||||
"Te rugăm să reîncepi cu /login",
|
chat_id=update.effective_chat.id,
|
||||||
parse_mode="Markdown"
|
text="Sesiune expirată\n\nConversația a expirat după 5 minute.\n\nIncearcă din nou cu /login"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Clear context
|
||||||
|
context.user_data.clear()
|
||||||
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,24 +54,65 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
auth_code = args[0].upper()
|
auth_code = args[0].upper()
|
||||||
logger.info(f"Attempting to link user {telegram_user_id} with code {auth_code}")
|
logger.info(f"Attempting to link user {telegram_user_id} with code {auth_code}")
|
||||||
|
|
||||||
# Show "linking..." message
|
# ȘTERGE mesajul utilizatorului imediat (chat curat)
|
||||||
linking_msg = await update.message.reply_text(
|
try:
|
||||||
"Linking contul...\n"
|
await update.message.delete()
|
||||||
"Te rog asteapta..."
|
except Exception as e:
|
||||||
)
|
logger.warning(f"Could not delete /start message: {e}")
|
||||||
|
|
||||||
|
# Check dacă user-ul a apăsat pe "Login din Web App" înainte
|
||||||
|
web_login_msg_id = context.user_data.get('web_login_message_id')
|
||||||
|
|
||||||
|
if web_login_msg_id:
|
||||||
|
# EDITEAZĂ mesajul existent cu "Login din Web App"
|
||||||
|
try:
|
||||||
|
await context.bot.edit_message_text(
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
message_id=web_login_msg_id,
|
||||||
|
text="Conectare cont...",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
linking_msg = await context.bot.get_updates() # Dummy, ne interesează doar message_id
|
||||||
|
# Simulăm un obiect Message pentru a putea folosi .edit_text() mai jos
|
||||||
|
class FakeMessage:
|
||||||
|
def __init__(self, chat_id, message_id, bot):
|
||||||
|
self.chat_id = chat_id
|
||||||
|
self.message_id = message_id
|
||||||
|
self._bot = bot
|
||||||
|
async def edit_text(self, text, reply_markup=None, parse_mode=None):
|
||||||
|
await self._bot.edit_message_text(
|
||||||
|
chat_id=self.chat_id,
|
||||||
|
message_id=self.message_id,
|
||||||
|
text=text,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=parse_mode
|
||||||
|
)
|
||||||
|
linking_msg = FakeMessage(update.effective_chat.id, web_login_msg_id, context.bot)
|
||||||
|
# Clear message_id din context
|
||||||
|
context.user_data.pop('web_login_message_id', None)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not edit web_login message: {e}")
|
||||||
|
# Fallback: creează mesaj nou
|
||||||
|
linking_msg = await update.effective_chat.send_message(
|
||||||
|
"Conectare cont...",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Nu există mesaj anterior - creează mesaj nou
|
||||||
|
linking_msg = await update.effective_chat.send_message(
|
||||||
|
"Conectare cont...",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
|
||||||
# Attempt linking
|
# Attempt linking
|
||||||
result = await link_telegram_account(telegram_user, auth_code)
|
result = await link_telegram_account(telegram_user, auth_code)
|
||||||
|
|
||||||
# Delete "linking..." message
|
|
||||||
await linking_msg.delete()
|
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
# Success!
|
# Success!
|
||||||
username = result['username']
|
username = result['username']
|
||||||
jwt_token = result['jwt_token']
|
jwt_token = result['jwt_token']
|
||||||
|
|
||||||
# Show main menu with buttons for newly linked user
|
# Get session and company info
|
||||||
session_manager = get_session_manager()
|
session_manager = get_session_manager()
|
||||||
session = await session_manager.get_or_create_session(telegram_user_id)
|
session = await session_manager.get_or_create_session(telegram_user_id)
|
||||||
company = session.get_active_company()
|
company = session.get_active_company()
|
||||||
@@ -92,24 +133,16 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
||||||
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True, cache_enabled=cache_enabled)
|
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True, cache_enabled=cache_enabled)
|
||||||
|
|
||||||
# Single welcome message with menu
|
# EDIT message to show menu with company
|
||||||
if company_name:
|
if company_name:
|
||||||
welcome_text = (
|
menu_text = f"{company_name}"
|
||||||
f"**Cont conectat cu succes**\n\n"
|
|
||||||
f"Bun venit, **{username}**!\n\n"
|
|
||||||
f"{company_name}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
welcome_text = (
|
menu_text = "Selectează o companie pentru a continua"
|
||||||
f"**Cont conectat cu succes**\n\n"
|
|
||||||
f"Bun venit, **{username}**!\n\n"
|
|
||||||
f"Selectează o companie pentru a continua"
|
|
||||||
)
|
|
||||||
|
|
||||||
welcome_message = pad_message_for_wide_buttons(welcome_text)
|
menu_message = pad_message_for_wide_buttons(menu_text)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await linking_msg.edit_text(
|
||||||
welcome_message,
|
menu_message,
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.MARKDOWN
|
||||||
)
|
)
|
||||||
@@ -117,12 +150,11 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
logger.info(f"User {telegram_user_id} successfully linked to {username}")
|
logger.info(f"User {telegram_user_id} successfully linked to {username}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Failed linking
|
# Failed linking - EDIT message to show error
|
||||||
await update.message.reply_text(
|
await linking_msg.edit_text(
|
||||||
"**Cod invalid sau expirat**\n\n"
|
"Cod invalid sau expirat\n\n"
|
||||||
"Generează un cod nou din aplicația web și trimite:\n"
|
"Generează un cod nou din aplicația web și trimite:\n"
|
||||||
"`/start CODUL_TAU`\n\n"
|
"/start CODUL_TAU",
|
||||||
"Codul expiră în 15 minute.",
|
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.MARKDOWN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user