CoolFace
Apppublic

Anil1515/ipo-automation

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
run_github_automation.py275 linesDownload Raw Back to root
1import os2import psycopg23import datetime4from playwright.sync_api import sync_playwright5from main import login, apply_ipo, handle_password_reset6from bank_checkers.bank import check_balance7 8# ── Firebase setup ──────────────────────────────────────────────────9import firebase_admin10from firebase_admin import credentials, messaging11 12MIN_BALANCE = 2000.0  # Minimum required balance to apply for IPO (Rs.)13 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            # Fallback: load from base64-encoded env variable (used on GitHub Actions)21            import base64, json, tempfile22            b64 = os.environ.get("FIREBASE_CREDENTIALS_B64", "")23            if not b64:24                print("  ⚠️  Firebase credentials not found (no file and no FIREBASE_CREDENTIALS_B64 env var). Skipping notifications.")25                return False26            cred_json = json.loads(base64.b64decode(b64).decode())27            cred = credentials.Certificate(cred_json)28        firebase_admin.initialize_app(cred)29    return True30 31def send_push_notification(fcm_tokens: list, title: str, body: str):32    if not fcm_tokens:33        print("  No FCM tokens – skipping notification.")34        return35    if not _init_firebase():36        return37    message = messaging.MulticastMessage(38        notification=messaging.Notification(title=title, body=body),39        tokens=fcm_tokens,40    )41    try:42        response = messaging.send_each_for_multicast(message)43        print(f"  FCM: {response.success_count} sent, {response.failure_count} failed.")44    except Exception as e:45        print(f"  FCM error: {e}")46 47# ── Database connection ─────────────────────────────────────────────48DB_URL = os.environ.get("DATABASE_URL")49 50# ── Encryption ─────────────────────────────────────────────────────51from cryptography.fernet import Fernet52 53_ENCRYPTION_KEY = os.environ.get("ENCRYPTION_KEY", "").encode()54 55def decrypt(token: str) -> str:56    if not token:57        return token58    if not _ENCRYPTION_KEY:59        if token.startswith('gAAAAA'):60            print("  ⚠️  WARNING: ENCRYPTION_KEY is not set, but token looks encrypted! MeroShare login will likely fail.")61        return token62    63    # If the token doesn't look encrypted (Fernet tokens start with gAAAAA), return as-is64    if not token.startswith('gAAAAA'):65        return token66 67    try:68        return Fernet(_ENCRYPTION_KEY).decrypt(token.encode()).decode()69    except Exception as e:70        print(f"  ⚠️  WARNING: Decryption failed for token starting with '{token[:10]}...' (Key mismatch?): {e}")71        return token72 73# ── Main automation ─────────────────────────────────────────────────74 75def run_automation():76    if not DB_URL:77        print("Error: DATABASE_URL not set.")78        return79 80    try:81        conn = psycopg2.connect(DB_URL)82        cur = conn.cursor()83 84        # 1. Fetch active accounts85        cur.execute("""86            SELECT a.id, a.meroshare_user, a.meroshare_pass, a.dp_name,87                   a.crn, a.tpin, a.bank_name, a.kitta, a.owner_id,88                   b.bank, b.phone_number, b.bank_password89            FROM automation_account a90            LEFT JOIN automation_bankaccount b ON b.linked_account_id = a.id91            WHERE a.is_active = True;92        """)93        columns = [desc[0] for desc in cur.description]94        accounts = [dict(zip(columns, row)) for row in cur.fetchall()]95 96        if not accounts:97            print("No active accounts found.")98            return99 100        print(f"Found {len(accounts)} active accounts. Starting automation...")101 102        with sync_playwright() as p:103            browser = p.chromium.launch(104                headless=True,105                args=[106                    '--disable-blink-features=AutomationControlled',107                    '--disable-infobars',108                    '--no-sandbox',109                    '--window-size=1280,720'110                ]111            )112 113            for acc in accounts:114                print(f"\n{'='*50}")115                print(f"Processing: {acc['meroshare_user']}")116                page = browser.new_page()117                status = "Error"118                remark = "Unknown error"119                ipo_name = "Auto-Check"  # Always initialise before try/finally120 121                try:122                    # 2. Bank balance check (if bank credentials are linked)123                    if acc.get('bank') and acc.get('phone_number') and acc.get('bank_password'):124                        bank_page = browser.new_page()125                        try:126                            balance = check_balance(127                                bank_code=acc['bank'],128                                phone_number=acc['phone_number'],129                                password=decrypt(acc['bank_password']),130                                page=bank_page,131                            )132                        except Exception as bank_err:133                            print(f"  ⚠️  Bank balance check raised exception: {bank_err}. Proceeding with IPO.")134                            balance = None135                        finally:136                            bank_page.close()137 138                        if balance is not None and balance < MIN_BALANCE:139                            print(f"  ⚠️  Balance Rs.{balance:.2f} < Rs.{MIN_BALANCE:.2f} — skipping IPO.")140                            status = "Skipped"141                            remark = f"Insufficient balance: Rs.{balance:.2f} (min Rs.{MIN_BALANCE:.2f})"142 143                            # Notify account holder144                            if acc.get('owner_id'):145                                cur.execute(146                                    "SELECT token FROM automation_fcmtoken WHERE user_id = %s",147                                    (acc['owner_id'],)148                                )149                                tokens = [row[0] for row in cur.fetchall()]150                                send_push_notification(151                                    tokens,152                                    acc['meroshare_user'],153                                    f"⚠️ Low Balance: Rs.{balance:.2f}. Please top up to apply for IPO.",154                                )155 156                            # Log and move on to next account157                            cur.execute("""158                                INSERT INTO automation_applicationlog159                                    (account_id, company_name, status, remark, timestamp, is_read)160                                VALUES (%s, %s, %s, %s, %s, %s)161                            """, (acc['id'], "Balance Check", status, remark,162                                  datetime.datetime.now(datetime.timezone.utc), False))163                            conn.commit()164                            page.close()165                            continue166 167                    # 3. Apply IPO via MeroShare168                    account_data = {169                        'MEROSHARE_USER': acc['meroshare_user'],170                        'MEROSHARE_PASS': decrypt(acc['meroshare_pass']),171                        'DP_NAME': acc['dp_name'],172                        'CRN': acc['crn'],173                        'TPIN': acc['tpin'],174                        'BANK_NAME': acc['bank_name'],175                        'KITTA': str(acc['kitta']),176                    }177 178                    page.goto("https://meroshare.cdsc.com.np", timeout=60000)179                    login_result = login(180                        page,181                        account_data['MEROSHARE_USER'],182                        account_data['MEROSHARE_PASS'],183                        account_data['DP_NAME'],184                    )185 186                    if login_result is True:187                        print(f"  ✅ Login OK. Applying IPO...")188                        success, result_detail = apply_ipo(page, account_data)189                        if success:190                            status = "Success"191                            ipo_name = result_detail192                            remark = f"{ipo_name} ipo has been applied successfully."193                        else:194                            status = "Failed"195                            remark = result_detail196                    elif login_result == "EXPIRED":197                        print(f"  ⚠️  Password Expired. Attempting automatic reset...")198                        if handle_password_reset(page, account_data):199                            print(f"  ✅ Password successfully reset and logged in.")200                            success, result_detail = apply_ipo(page, account_data)201                            if success:202                                status = "Success"203                                ipo_name = result_detail204                                remark = f"{ipo_name} (after password reset) ipo has been applied successfully."205                            else:206                                status = "Failed"207                                remark = result_detail208                        else:209                            print(f"  ❌ Password reset failed.")210                            status = "Failed"211                            remark = "Password expired and automatic reset failed."212                    else:213                        print(f"  ❌ Login failed: {login_result}")214                        status = "Failed"215                        remark = f"Login failed: {login_result}"216 217                except Exception as e:218                    print(f"  ❌ Exception: {e}")219                    status = "Error"220                    remark = str(e)221 222                finally:223                    # 4. Write log and send notification — wrapped so a DB error here224                    #    does NOT prevent the next account from being processed.225                    try:226                        cur.execute("""227                            INSERT INTO automation_applicationlog228                                (account_id, company_name, status, remark, timestamp, is_read)229                            VALUES (%s, %s, %s, %s, %s, %s)230                        """, (acc['id'], ipo_name, status, remark,231                              datetime.datetime.now(datetime.timezone.utc), False))232                        conn.commit()233                    except Exception as db_err:234                        print(f"  ⚠️  DB log error for {acc['meroshare_user']}: {db_err}")235                        try:236                            conn.rollback()237                        except Exception:238                            pass239 240                    try:241                        if acc.get('owner_id'):242                            cur.execute(243                                "SELECT token FROM automation_fcmtoken WHERE user_id = %s",244                                (acc['owner_id'],)245                            )246                            tokens = [row[0] for row in cur.fetchall()]247                            if status == "Success":248                                notif_title = acc['meroshare_user']249                                notif_body = f"✅ Success: {ipo_name} applied successfully."250                                send_push_notification(tokens, notif_title, notif_body)251                            elif remark != "No ordinary shares found":252                                notif_title = acc['meroshare_user']253                                notif_body = f"⚠️ {status}: {remark}"254                                send_push_notification(tokens, notif_title, notif_body)255                    except Exception as notif_err:256                        print(f"  ⚠️  Notification error for {acc['meroshare_user']}: {notif_err}")257 258                    try:259                        page.close()260                    except Exception:261                        pass262 263            browser.close()264 265        cur.close()266        conn.close()267        print("\nAutomation run completed.")268 269    except Exception as e:270        print(f"DB Error: {e}")271 272 273if __name__ == "__main__":274    run_automation()275