import os import smtplib import httpx from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from datetime import datetime # ── 환경변수 ────────────────────────────────────────────── DISCORD_WEBHOOK_URL = os.getenv("DISCORD_WEBHOOK_URL", "") GMAIL_USER = os.getenv("GMAIL_USER", "") GMAIL_APP_PASSWORD = os.getenv("GMAIL_APP_PASSWORD", "") ALERT_EMAIL_TO = os.getenv("ALERT_EMAIL_TO", "") # ── Discord ─────────────────────────────────────────────── async def send_discord(title: str, message: str, color: int = 0xe74c3c): if not DISCORD_WEBHOOK_URL: print("[NOTIFIER] Discord webhook URL not set, skipping") return payload = { "embeds": [{ "title": title, "description": message, "color": color, "footer": {"text": "Web Portal Monitor"}, "timestamp": datetime.utcnow().isoformat() }] } try: async with httpx.AsyncClient() as client: res = await client.post(DISCORD_WEBHOOK_URL, json=payload, timeout=10) if res.status_code not in (200, 204): print(f"[NOTIFIER] Discord error: {res.status_code} {res.text}") else: print(f"[NOTIFIER] Discord sent: {title}") except Exception as e: print(f"[NOTIFIER] Discord exception: {e}") # ── Gmail ───────────────────────────────────────────────── def send_email(subject: str, body: str): if not all([GMAIL_USER, GMAIL_APP_PASSWORD, ALERT_EMAIL_TO]): print("[NOTIFIER] Gmail config not set, skipping") return try: msg = MIMEMultipart("alternative") msg["Subject"] = f"[Web Portal] {subject}" msg["From"] = GMAIL_USER msg["To"] = ALERT_EMAIL_TO html = f"""

⚠️ {subject}

{body}


Web Portal Monitor · {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
""" msg.attach(MIMEText(html, "html")) with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp: smtp.login(GMAIL_USER, GMAIL_APP_PASSWORD) smtp.sendmail(GMAIL_USER, ALERT_EMAIL_TO, msg.as_string()) print(f"[NOTIFIER] Email sent: {subject}") except Exception as e: print(f"[NOTIFIER] Email exception: {e}") # ── 채널별 알림 함수 ────────────────────────────────────── import asyncio async def notify_both(title: str, message: str, color: int = 0xe74c3c): """Discord + Gmail 둘 다 전송 (Pod 이상/복구)""" await send_discord(title, message, color) loop = asyncio.get_event_loop() await loop.run_in_executor(None, send_email, title, message) async def notify_email_only(title: str, message: str, color: int = 0xf39c12): """Gmail만 전송 (인증서 만료 임박)""" loop = asyncio.get_event_loop() await loop.run_in_executor(None, send_email, title, message) async def notify_discord_only(title: str, message: str, color: int = 0xe74c3c): """Discord만 전송 (계정 잠금, 임시 비밀번호 발급)""" await send_discord(title, message, color)