Bishall10/ipo
0
1"""2notifications.py3----------------4Email notification utilities for the IPO automation.5"""6 7import os8import smtplib9from email.mime.text import MIMEText10from email.mime.multipart import MIMEMultipart11from dotenv import load_dotenv12 13load_dotenv()14 15 16def send_email_notification(to_email, subject, message):17 """18 Sends an email notification via Gmail SMTP.19 Reads SENDER_EMAIL, SENDER_PASSWORD, SMTP_SERVER, SMTP_PORT from .env.20 Silently skips if credentials are missing or no recipient is given.21 """22 if not to_email:23 return24 25 # 1. Try Google Apps Script (Web API) first - Works on Hugging Face26 gmail_api_url = os.getenv("GMAIL_API_URL")27 if gmail_api_url:28 try:29 import requests30 response = requests.post(31 gmail_api_url,32 json={"to": to_email, "subject": subject, "body": message},33 timeout=1034 )35 if response.status_code == 200:36 print(f"✅ Email sent via Google Apps Script to {to_email}")37 return True38 else:39 print(f"❌ Google Apps Script Error: {response.text}")40 except Exception as e:41 print(f"❌ Google Apps Script Exception: {str(e)}")42 43 # 2. Fallback to SMTP44 sender_email = os.getenv("SENDER_EMAIL")45 sender_password = os.getenv("SENDER_PASSWORD")46 smtp_server = os.getenv("SMTP_SERVER") or "smtp.gmail.com"47 smtp_port = int(os.getenv("SMTP_PORT") or 465)48 49 if not (sender_email and sender_password or gmail_api_url):50 print("Warning: Skipping email notification (No SENDER_EMAIL or GMAIL_API_URL found)")51 return False52 53 try:54 msg = MIMEMultipart()55 msg["From"] = f"IPO Automation <{sender_email}>"56 msg["To"] = to_email57 msg["Subject"] = subject58 msg.attach(MIMEText(message, "plain"))59 60 if sender_email and sender_password:61 server = smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=10)62 server.login(sender_email, sender_password)63 server.send_message(msg)64 server.quit()65 print(f"✅ Email sent via SMTP to {to_email}")66 return True67 return False68 except Exception as e:69 print(f"❌ SMTP Error: {str(e)}")70 return False71 72 73def send_push_notification(tokens, title, body):74 """75 Sends FCM Push Notification to list of tokens.76 """77 if not tokens:78 return79 80 try:81 import firebase_admin82 from firebase_admin import credentials, messaging83 84 if not firebase_admin._apps:85 # Look for config in default location86 cred_path = os.path.join(os.path.dirname(__file__), "config", "firebase_vcc.json")87 if os.path.exists(cred_path):88 cred = credentials.Certificate(cred_path)89 else:90 # Fallback: load from base64-encoded env variable (GitHub Actions)91 import base64, json92 b64 = os.environ.get("FIREBASE_CREDENTIALS_B64", "")93 if not b64:94 print(f"Warning: Firebase credentials not found. Set FIREBASE_CREDENTIALS_B64 env var. Skipping push notification.")95 return96 cred_json = json.loads(base64.b64decode(b64).decode())97 cred = credentials.Certificate(cred_json)98 firebase_admin.initialize_app(cred)99 100 android_config = messaging.AndroidConfig(101 priority='high',102 notification=messaging.AndroidNotification(103 channel_id='high_importance_channel',104 sticky=True,105 default_vibrate_timings=True,106 default_sound=True,107 )108 )109 110 message = messaging.MulticastMessage(111 notification=messaging.Notification(title=title, body=body),112 tokens=tokens,113 android=android_config,114 )115 response = messaging.send_each_for_multicast(message)116 print(f"Push Notification Sent: {response.success_count} success, {response.failure_count} failure")117 return response118 except Exception as e:119 print(f"Warning: Failed to send push notification: {e}")120 121 122def broadcast_push_notification(title, body):123 """124 Fetches all unique FCM tokens from the database and sends a broadcast message.125 """126 db_url = os.getenv("DATABASE_URL")127 if not db_url:128 print("Error: DATABASE_URL not found. Cannot broadcast.")129 return130 131 try:132 import psycopg2133 conn = psycopg2.connect(db_url)134 cur = conn.cursor()135 cur.execute("SELECT DISTINCT token FROM automation_fcmtoken")136 tokens = [t[0] for t in cur.fetchall()]137 cur.close()138 conn.close()139 140 if tokens:141 print(f"Broadcasting to {len(tokens)} tokens...")142 return send_push_notification(tokens, title, body)143 else:144 print("No tokens found to broadcast.")145 except Exception as e:146 print(f"Error in broadcast: {e}")147 148 149def get_fcm_tokens_for_user(username, owner_id=None):150 """151 Fetches all FCM tokens for a user, using owner_id if provided, 152 or falling back to lookup by meroshare username.153 """154 db_url = os.getenv("DATABASE_URL")155 if not db_url:156 return []157 try:158 import psycopg2159 conn = psycopg2.connect(db_url)160 cur = conn.cursor()161 162 # If owner_id is not provided, look it up from the account table163 if not owner_id and username:164 cur.execute("SELECT owner_id FROM automation_account WHERE meroshare_user = %s", (username,))165 row = cur.fetchone()166 if row:167 owner_id = row[0]168 169 if owner_id:170 cur.execute("SELECT token FROM automation_fcmtoken WHERE user_id = %s", (owner_id,))171 tokens = [r[0] for r in cur.fetchall()]172 cur.close()173 conn.close()174 return tokens175 176 cur.close()177 conn.close()178 except Exception as e:179 print(f"Error fetching FCM tokens for user {username}: {e}")180 return []181 182 