Bishall10/ipo
0
1import os2import psycopg23import datetime4import time5import re6from playwright.sync_api import sync_playwright7from main import login, apply_ipo, handle_password_reset, check_allotment_results8from expiry_handler import handle_expired_account9 10# ── Firebase setup ──────────────────────────────────────────────────11import firebase_admin12from firebase_admin import credentials, messaging13 14def _init_firebase():15 if not firebase_admin._apps:16 cred_path = os.path.join(os.path.dirname(__file__), "config", "firebase_vcc.json")17 if os.path.exists(cred_path):18 cred = credentials.Certificate(cred_path)19 else:20 import base64, json21 b64 = os.environ.get("FIREBASE_CREDENTIALS_B64", "")22 if not b64:23 print(" ⚠️ Firebase credentials not found. Skipping notifications.")24 return False25 cred_json = json.loads(base64.b64decode(b64).decode())26 cred = credentials.Certificate(cred_json)27 firebase_admin.initialize_app(cred)28 return True29 30def send_push_notification(fcm_tokens: list, title: str, body: str):31 if not fcm_tokens:32 return33 if not _init_firebase():34 return35 android_config = messaging.AndroidConfig(36 priority='high',37 notification=messaging.AndroidNotification(38 channel_id='high_importance_channel',39 sticky=True,40 default_vibrate_timings=True,41 default_sound=True,42 )43 )44 message = messaging.MulticastMessage(45 notification=messaging.Notification(title=title, body=body),46 tokens=fcm_tokens,47 android=android_config,48 )49 try:50 response = messaging.send_each_for_multicast(message)51 print(f" FCM: {response.success_count} sent, {response.failure_count} failed.")52 except Exception as e:53 print(f" FCM error: {e}")54 55# ── Database connection ─────────────────────────────────────────────56DB_URL = os.environ.get("DATABASE_URL")57 58# ── Encryption ─────────────────────────────────────────────────────59from cryptography.fernet import Fernet60_ENCRYPTION_KEY = os.environ.get("ENCRYPTION_KEY", "").encode()61 62def decrypt(token: str) -> str:63 if not token:64 return token65 if not _ENCRYPTION_KEY:66 return token67 if not token.startswith('gAAAAA'):68 return token69 try:70 return Fernet(_ENCRYPTION_KEY).decrypt(token.encode()).decode()71 except Exception as e:72 print(f" ⚠️ Decryption failed: {e}")73 return token74 75# ── Main automation ─────────────────────────────────────────────────76 77def run_automation():78 if not DB_URL:79 print("Error: DATABASE_URL not set.")80 return81 82 try:83 # 1. Fetch active accounts84 conn = psycopg2.connect(DB_URL)85 cur = conn.cursor()86 cur.execute("""87 SELECT a.id, a.meroshare_user, a.meroshare_pass, a.dp_name,88 a.crn, a.tpin, a.bank_name, a.kitta, a.owner_id89 FROM automation_account a90 WHERE a.is_active = True;91 """)92 columns = [desc[0] for desc in cur.description]93 accounts = [dict(zip(columns, row)) for row in cur.fetchall()]94 cur.close()95 conn.close()96 97 if not accounts:98 print("No active accounts found.")99 return100 101 print(f"Found {len(accounts)} active accounts. Starting automation...")102 103 with sync_playwright() as p:104 browser = p.chromium.launch(105 headless=True,106 args=['--disable-blink-features=AutomationControlled', '--no-sandbox']107 )108 context = browser.new_context(109 user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",110 viewport={"width": 1280, "height": 720},111 permissions=['geolocation'],112 geolocation={'latitude': 27.7172, 'longitude': 85.3240}113 )114 115 for acc in accounts:116 print(f"\n{'='*50}\nProcessing: {acc['meroshare_user']}")117 notification_sent = False118 status = "Error"119 remark = "Unknown error"120 ipo_name = "Auto-Check"121 page = context.new_page()122 try:123 decrypted_pass = decrypt(acc['meroshare_pass'])124 if decrypted_pass.startswith('gAAAAA'):125 print(f" ⚠️ WARNING: Password for {acc['meroshare_user']} still looks ENCRYPTED. Check your ENCRYPTION_KEY.")126 else:127 print(f" 🔑 Password Decrypted. Length: {len(decrypted_pass)} chars.")128 129 account_data = {130 'ID': acc['id'],131 'MEROSHARE_USER': acc['meroshare_user'],132 'MEROSHARE_PASS': decrypted_pass,133 'DP_NAME': acc['dp_name'],134 'CRN': acc['crn'],135 'TPIN': acc['tpin'],136 'BANK_NAME': acc['bank_name'],137 'KITTA': str(acc['kitta']),138 'owner_id': acc.get('owner_id'),139 }140 141 page.goto("https://meroshare.cdsc.com.np", timeout=60000)142 login_result = login(page, account_data['MEROSHARE_USER'], account_data['MEROSHARE_PASS'], account_data['DP_NAME'])143 144 if login_result is True:145 print(f" ✅ Login OK. Applying IPO...")146 success, result_detail = apply_ipo(page, account_data)147 if success:148 status = "Success"149 ipo_name = result_detail150 remark = f"{ipo_name} applied successfully."151 else:152 status = "Failed"153 remark = result_detail154 155 # Call allotment result check after applying156 print(f" Checking allotment status in MeroShare dashboard...")157 try:158 check_allotment_results(page, account_data)159 except Exception as ar_e:160 print(f" ⚠️ Error checking allotment results: {ar_e}")161 elif login_result == "EXPIRED":162 print(f" [{acc['meroshare_user']}] Password expired. Handling reset...")163 if handle_password_reset(page, account_data):164 status = "Success"165 remark = "Password reset successfully. Re-run required."166 else:167 status = "Failed"168 remark = "Password expired and reset failed."169 else:170 print(f" ❌ MeroShare Login failed for {acc['meroshare_user']}.")171 status = "Failed"172 remark = "MeroShare login failed"173 174 except Exception as e:175 print(f" ❌ Inner Exception: {e}")176 remark = str(e)177 finally:178 # 3. Final Logging and Notification179 try:180 conn = psycopg2.connect(DB_URL)181 cur = conn.cursor()182 # Skip logging for: no IPO found, and Block Amount Status updates (these are noise)183 remark_lower = remark.lower()184 skip_log = (185 remark == "No ordinary shares found" or186 "block amount status" in remark_lower187 )188 if not skip_log:189 cur.execute("""190 INSERT INTO automation_applicationlog191 (account_id, company_name, status, remark, timestamp, is_read, is_listed)192 VALUES (%s, %s, %s, %s, %s, %s, %s)193 """, (acc['id'], ipo_name, status, remark, datetime.datetime.now(datetime.timezone.utc), False, False))194 conn.commit()195 else:196 print(f" ℹ️ Skipping database log for: {remark}")197 198 cur.close()199 conn.close()200 except Exception as fatal:201 print(f" ⚠️ Fatal Logging Error: {fatal}")202 203 try: page.close()204 except: pass205 206 browser.close()207 print("\nAutomation run completed.")208 209 except Exception as e:210 print(f"Global Error: {e}")211 212if __name__ == "__main__":213 run_automation()214 