feat: Discord/Gmail 알림 기능 추가
Some checks failed
Build and Push Images / build-backend (push) Has been cancelled
Some checks failed
Build and Push Images / build-backend (push) Has been cancelled
This commit is contained in:
85
backend/notifier.py
Executable file
85
backend/notifier.py
Executable file
@@ -0,0 +1,85 @@
|
||||
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"""
|
||||
<html><body style="font-family:sans-serif;padding:20px">
|
||||
<div style="background:#e74c3c;color:white;padding:12px 20px;border-radius:8px 8px 0 0">
|
||||
<h2 style="margin:0">⚠️ {subject}</h2>
|
||||
</div>
|
||||
<div style="border:1px solid #ddd;border-top:none;padding:20px;border-radius:0 0 8px 8px">
|
||||
<p style="white-space:pre-line">{body}</p>
|
||||
<hr/>
|
||||
<small style="color:#999">Web Portal Monitor · {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</small>
|
||||
</div>
|
||||
</body></html>
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user