fix telegram
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Async SMTP Email Service with retry logic and proper error handling
|
||||
"""
|
||||
import aiosmtplib
|
||||
from email.message import EmailMessage
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EmailService:
|
||||
"""Async SMTP client for sending authentication codes"""
|
||||
|
||||
def __init__(self):
|
||||
self.smtp_host = os.getenv("SMTP_HOST", "mail.romfast.ro")
|
||||
self.smtp_port = int(os.getenv("SMTP_PORT", "587"))
|
||||
self.smtp_user = os.getenv("SMTP_USER")
|
||||
self.smtp_password = os.getenv("SMTP_PASSWORD")
|
||||
self.from_email = os.getenv("SMTP_FROM_EMAIL")
|
||||
self.from_name = os.getenv("SMTP_FROM_NAME", "ROA2WEB")
|
||||
self.use_tls = os.getenv("SMTP_USE_TLS", "true").lower() == "true"
|
||||
|
||||
# Retry configuration
|
||||
self.max_retries = int(os.getenv("EMAIL_MAX_RETRIES", "3"))
|
||||
self.retry_delay = float(os.getenv("EMAIL_RETRY_DELAY", "2.0")) # seconds
|
||||
|
||||
# Validate required config
|
||||
if not all([self.smtp_user, self.smtp_password, self.from_email]):
|
||||
raise ValueError("SMTP configuration incomplete. Check .env file.")
|
||||
|
||||
async def send_auth_code(
|
||||
self,
|
||||
to_email: str,
|
||||
code: str,
|
||||
username: str
|
||||
) -> bool:
|
||||
"""
|
||||
Send authentication code via email with retry logic
|
||||
|
||||
Args:
|
||||
to_email: Recipient email address
|
||||
code: 6-digit authentication code
|
||||
username: Oracle username for personalization
|
||||
|
||||
Returns:
|
||||
True if email sent successfully (after retries if needed)
|
||||
|
||||
Raises:
|
||||
No exceptions - returns False on all failures
|
||||
"""
|
||||
subject = "Autentificare ROA2WEB"
|
||||
text_body = self._create_email_template(code, username)
|
||||
|
||||
for attempt in range(1, self.max_retries + 1):
|
||||
try:
|
||||
await self._send_email(to_email, subject, text_body)
|
||||
logger.info(
|
||||
f"[EMAIL] ✅ Sent auth code to {to_email} "
|
||||
f"(attempt {attempt}/{self.max_retries}) via {self.smtp_host}:{self.smtp_port}"
|
||||
)
|
||||
return True
|
||||
|
||||
except aiosmtplib.SMTPException as e:
|
||||
logger.error(
|
||||
f"[EMAIL] ❌ Attempt {attempt}/{self.max_retries} failed for {to_email}: "
|
||||
f"{type(e).__name__}: {e}"
|
||||
)
|
||||
if attempt < self.max_retries:
|
||||
# Exponential backoff: 2s, 4s, 8s
|
||||
delay = self.retry_delay * (2 ** (attempt - 1))
|
||||
logger.info(f"[EMAIL] Retrying in {delay}s...")
|
||||
await asyncio.sleep(delay)
|
||||
else:
|
||||
logger.error(f"[EMAIL] ❌ All {self.max_retries} attempts failed for {to_email}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[EMAIL] ❌ Unexpected error on attempt {attempt}/{self.max_retries} for {to_email}: {type(e).__name__}: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
async def _send_email(
|
||||
self,
|
||||
to_email: str,
|
||||
subject: str,
|
||||
text_body: str
|
||||
) -> None:
|
||||
"""
|
||||
Internal async SMTP sender (plain text to avoid spam filters)
|
||||
|
||||
Raises:
|
||||
aiosmtplib.SMTPException: On SMTP errors
|
||||
"""
|
||||
message = EmailMessage()
|
||||
message["From"] = f"{self.from_name} <{self.from_email}>"
|
||||
message["To"] = to_email
|
||||
message["Subject"] = subject
|
||||
message.set_content(text_body)
|
||||
|
||||
smtp = aiosmtplib.SMTP(
|
||||
hostname=self.smtp_host,
|
||||
port=self.smtp_port,
|
||||
start_tls=self.use_tls,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
try:
|
||||
await smtp.connect()
|
||||
await smtp.login(self.smtp_user, self.smtp_password)
|
||||
await smtp.send_message(message)
|
||||
finally:
|
||||
try:
|
||||
await smtp.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def _create_email_template(self, code: str, username: str) -> str:
|
||||
"""Generate plain text email body (HTML blocked by spam filters)"""
|
||||
return (
|
||||
f"Codul tau de autentificare ROA2WEB:\n\n"
|
||||
f" {code}\n\n"
|
||||
f"Introdu acest cod in Telegram. Expira in 5 minute.\n\n"
|
||||
f"---\n"
|
||||
f"Solicitat pentru: {username}\n"
|
||||
f"Daca nu ai initiat aceasta autentificare, ignora acest email."
|
||||
)
|
||||
|
||||
|
||||
# Singleton instance
|
||||
_email_service: Optional[EmailService] = None
|
||||
|
||||
|
||||
def get_email_service() -> EmailService:
|
||||
"""Get or create singleton email service instance"""
|
||||
global _email_service
|
||||
if _email_service is None:
|
||||
_email_service = EmailService()
|
||||
return _email_service
|
||||
Reference in New Issue
Block a user