feat: add Telegram notifications for user signup and Stripe events
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m34s

This commit is contained in:
2026-06-07 11:37:01 +02:00
parent 8659b6761f
commit 29753881a6
3 changed files with 122 additions and 0 deletions

View File

@@ -272,6 +272,21 @@ async def register_v1(request: Request):
try:
user = create_user(user_create)
# Alert admin via Telegram on signup
try:
from utils.telegram import send_telegram_notification
import asyncio
msg = (
f"👤 *Nouvelle inscription !*\n\n"
f"• *Email* : `{user.email}`\n"
f"• *Nom* : `{user.name or 'Non renseigné'}`\n"
f"• *ID* : `{user.id}`\n"
f"• *Forfait* : `{user.plan.value}`\n"
f"• *Date* : {datetime.now(timezone.utc).strftime('%d/%m/%Y %H:%M:%S')} UTC"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification: {tel_err}")
except ValueError as exc:
msg = str(exc).strip().lower()
if "email already registered" in msg or "email already" in msg:

View File

@@ -336,6 +336,21 @@ async def handle_checkout_completed(session: Dict):
)
except Exception as e:
logger.error("Failed to send credit purchase email: %s", e)
# Send Telegram notification
try:
from utils.telegram import send_telegram_notification
import asyncio
msg = (
f"💰 *Achat de crédits !*\n\n"
f"• *Utilisateur* : `{user.name or 'Non renseigné'}` (`{user.email}`)\n"
f"• *Crédits achetés* : `{credits}`\n"
f"• *Montant* : `{session.get('amount_total', 0) / 100:.2f} {session.get('currency', 'eur').upper()}`\n"
f"• *ID Transaction* : `{session_id}`"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification for credits checkout: {tel_err}")
return
# It's a subscription
@@ -420,6 +435,22 @@ async def handle_checkout_completed(session: Dict):
except Exception as e:
logger.error("Failed to send activation email: %s", e)
# Send Telegram notification
try:
from utils.telegram import send_telegram_notification
import asyncio
ends_str = subscription_ends_at.strftime("%d/%m/%Y") if subscription_ends_at else "Non spécifiée"
msg = (
f"⭐ *Nouvel Abonnement Activé !*\n\n"
f"• *Utilisateur* : `{user.name or 'Non renseigné'}` (`{user.email}`)\n"
f"• *Forfait* : `{plan.upper()}`\n"
f"• *Montant* : `{session.get('amount_total', 0) / 100:.2f} {session.get('currency', 'eur').upper()}`\n"
f"• *Date de fin* : {ends_str}"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification for subscription activation: {tel_err}")
async def handle_subscription_updated(subscription: Dict):
"""Handle subscription updates"""
@@ -474,6 +505,19 @@ async def handle_subscription_updated(subscription: Dict):
except Exception as e:
logger.error("Failed to send cancellation email in handle_subscription_updated: %s", e)
# Send Telegram notification for cancellation request
try:
from utils.telegram import send_telegram_notification
import asyncio
msg = (
f"🔕 *Désinscription programmée (Stripe)*\n\n"
f"• *Utilisateur* : `{user.name or 'Non renseigné'}` (`{user.email}`)\n"
f"• *Date de fin d'accès* : {ends_str}"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification for subscription cancellation request: {tel_err}")
async def handle_subscription_deleted(subscription: Dict):
"""Handle subscription cancellation (actually ended)"""
@@ -509,6 +553,19 @@ async def handle_subscription_deleted(subscription: Dict):
except Exception as e:
logger.error("Failed to send subscription ended email: %s", e)
# Send Telegram notification for ended subscription
try:
from utils.telegram import send_telegram_notification
import asyncio
msg = (
f"❌ *Abonnement Terminé / Expiré (Stripe)*\n\n"
f"• *Utilisateur* : `{user.name or 'Non renseigné'}` (`{user.email}`)\n"
f"• *Statut* : Retour au forfait `FREE`"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification for subscription ended: {tel_err}")
async def handle_payment_failed(invoice: Dict):
"""Handle failed payment — set subscription status to PAST_DUE and send email"""
@@ -547,6 +604,19 @@ async def handle_payment_failed(invoice: Dict):
)
except Exception as e:
logger.error("Failed to send payment failed email: %s", e)
# Send Telegram notification
try:
from utils.telegram import send_telegram_notification
import asyncio
msg = (
f"⚠️ *Échec de paiement (Stripe)*\n\n"
f"• *Utilisateur* : `{db_user.name or 'Non renseigné'}` (`{db_user.email}`)\n"
f"• *Statut* : Facture impayée, forfait passé en `PAST_DUE`"
)
asyncio.create_task(send_telegram_notification(msg))
except Exception as tel_err:
logger.error(f"Failed to send telegram notification for payment failed: {tel_err}")
except Exception as exc:
logger.error("handle_payment_failed DB error: %s", exc)

37
utils/telegram.py Normal file
View File

@@ -0,0 +1,37 @@
import os
import logging
import httpx
logger = logging.getLogger(__name__)
async def send_telegram_notification(message: str) -> bool:
"""
Envoie un message formaté en Markdown à l'administrateur via Telegram.
Utilise les variables d'environnement TELEGRAM_BOT_TOKEN et TELEGRAM_CHAT_ID.
"""
bot_token = os.getenv("TELEGRAM_BOT_TOKEN")
chat_id = os.getenv("TELEGRAM_CHAT_ID")
if not bot_token or not chat_id:
logger.warning("Notification Telegram ignorée : TELEGRAM_BOT_TOKEN ou TELEGRAM_CHAT_ID non configuré.")
return False
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
payload = {
"chat_id": chat_id,
"text": message,
"parse_mode": "Markdown"
}
try:
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(url, json=payload)
if resp.status_code == 200:
logger.info("Notification Telegram envoyée avec succès.")
return True
else:
logger.error(f"Échec de l'envoi Telegram (HTTP {resp.status_code}): {resp.text}")
return False
except Exception as e:
logger.error(f"Erreur lors de l'envoi de la notification Telegram : {e}")
return False