Spaces:
Sleeping
Sleeping
| from flask import Flask, render_template, request, redirect, url_for, jsonify, session | |
| from flask_sqlalchemy import SQLAlchemy | |
| from flask_socketio import SocketIO, emit | |
| import uuid | |
| from datetime import datetime, timezone | |
| import requests | |
| import time | |
| import threading | |
| from cryptography.fernet import Fernet | |
| import os | |
| import random | |
| import string | |
| STOCK_START_TIME = None | |
| STOCK_START_PRICE_AN = 1.27 | |
| STOCK_START_PRICE_ASTRAFOODS = 2.50 | |
| STOCK_START_PRICE_NOVASWEETS = 0.85 | |
| def get_an_price(): | |
| if STOCK_START_TIME is None: | |
| return STOCK_START_PRICE_AN | |
| elapsed = time.time() - STOCK_START_TIME | |
| steps = int(elapsed / 0.5) | |
| if steps == 0: | |
| return STOCK_START_PRICE_AN | |
| random.seed(int(STOCK_START_TIME)) | |
| price = STOCK_START_PRICE_AN | |
| for i in range(steps): | |
| volatility = random.uniform(-0.15, 0.15) | |
| trend = 0.000014 | |
| change = volatility + trend | |
| price += change | |
| if price < 0.01: | |
| price = 0.01 | |
| return round(price, 2) | |
| def get_astrafoods_price(): | |
| if STOCK_START_TIME is None: | |
| return STOCK_START_PRICE_ASTRAFOODS | |
| elapsed = time.time() - STOCK_START_TIME | |
| steps = int(elapsed / 0.5) | |
| if steps == 0: | |
| return STOCK_START_PRICE_ASTRAFOODS | |
| random.seed(int(STOCK_START_TIME) + 1000) | |
| price = STOCK_START_PRICE_ASTRAFOODS | |
| for i in range(steps): | |
| volatility = random.uniform(-0.02, 0.02) | |
| trend = 0.0000028 | |
| change = volatility + trend | |
| price += change | |
| if price < 0.01: | |
| price = 0.01 | |
| return round(price, 2) | |
| def get_novasweets_price(): | |
| if STOCK_START_TIME is None: | |
| return STOCK_START_PRICE_NOVASWEETS | |
| elapsed = time.time() - STOCK_START_TIME | |
| steps = int(elapsed / 0.5) | |
| if steps == 0: | |
| return STOCK_START_PRICE_NOVASWEETS | |
| random.seed(int(STOCK_START_TIME) + 2000) | |
| price = STOCK_START_PRICE_NOVASWEETS | |
| for i in range(steps): | |
| volatility = random.uniform(-0.20, 0.20) | |
| trend = 0.000021 | |
| change = volatility + trend | |
| price += change | |
| if price < 0.01: | |
| price = 0.01 | |
| return round(price, 2) | |
| app = Flask(__name__) | |
| app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///astrapay.db' | |
| app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | |
| app.config['SECRET_KEY'] = 'your-secret-key-here' | |
| db = SQLAlchemy(app) | |
| socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading') | |
| def background_stock_updates(): | |
| while True: | |
| if STOCK_START_TIME is not None: | |
| price_an = get_an_price() | |
| price_astrafoods = get_astrafoods_price() | |
| price_novasweets = get_novasweets_price() | |
| socketio.emit('stock_update', { | |
| 'an': price_an, | |
| 'astrafoods': price_astrafoods, | |
| 'novasweets': price_novasweets | |
| }) | |
| time.sleep(0.5) | |
| def get_encryption_key(): | |
| key_file = 'encryption.key' | |
| if os.path.exists(key_file): | |
| with open(key_file, 'rb') as f: | |
| return f.read() | |
| else: | |
| key = Fernet.generate_key() | |
| with open(key_file, 'wb') as f: | |
| f.write(key) | |
| return key | |
| ENCRYPTION_KEY = get_encryption_key() | |
| cipher_suite = Fernet(ENCRYPTION_KEY) | |
| def encrypt_value(value): | |
| if isinstance(value, str): | |
| value = value.encode() | |
| return cipher_suite.encrypt(value).decode() | |
| def decrypt_value(encrypted_value): | |
| return cipher_suite.decrypt(encrypted_value.encode()).decode() | |
| def calculate_fee(amount): | |
| """Calculate astratrader fee based on tiered structure""" | |
| if amount < 10: | |
| return 0 | |
| elif amount < 25: | |
| return 1 | |
| elif amount < 100: | |
| return 3 | |
| elif amount < 250: | |
| return 5 | |
| elif amount < 300: | |
| return 6 | |
| elif amount < 500: | |
| return 8 | |
| elif amount < 1000: | |
| return 10 | |
| elif amount < 2500: | |
| return 22 | |
| elif amount < 3000: | |
| return 24 | |
| elif amount < 5000: | |
| return 24 | |
| else: | |
| return 50 | |
| def calculate_claim_amounts(amount): | |
| """Calculate send amount (+1%) and claim amount (-1%) for claim links""" | |
| send_amount = max(1, int(amount * 1.01)) | |
| claim_amount = max(1, int(amount * 0.99)) | |
| return send_amount, claim_amount | |
| def generate_unique_code(length=4): | |
| """Generate a unique alphanumeric code (must be called within app context)""" | |
| chars = string.ascii_uppercase + string.digits | |
| max_attempts = 100 | |
| for _ in range(max_attempts): | |
| code = ''.join(random.choice(chars) for _ in range(length)) | |
| if not PaymentLink.query.filter_by(id=code).first() and not ClaimLink.query.filter_by(id=code).first(): | |
| return code | |
| return ''.join(random.choice(chars) for _ in range(length)) | |
| ENCRYPTED_CONNECT_SID = encrypt_value('s%3AchprkBWT1gzUWdCkJcWneCauTP73rb1-.tLvwxPgi6HFX3f1ULUt120nsbCJgODtlWpdp0cIluM0a') | |
| ENCRYPTED_API_URL = encrypt_value('https://astra-bank-moh1812.replit.app/api/transactions') | |
| ENCRYPTED_REFERER = encrypt_value('https://astra-bank-moh1812.replit.app/') | |
| ENCRYPTED_ETAG = encrypt_value('W/"6e3-L0zHI4rHMa4nHmyewyA/4y+lL6c"') | |
| ENCRYPTED_BANK_BASE_URL = encrypt_value('https://astra-bank-moh1812.replit.app') | |
| class PaymentLink(db.Model): | |
| id = db.Column(db.String(4), primary_key=True) | |
| amount = db.Column(db.Integer, nullable=False) | |
| description = db.Column(db.String(500), nullable=False) | |
| recipient_email = db.Column(db.String(255), nullable=True) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow) | |
| paid = db.Column(db.Boolean, default=False) | |
| def __repr__(self): | |
| return f'<PaymentLink {self.id}: {self.amount} Astras>' | |
| class ClaimLink(db.Model): | |
| id = db.Column(db.String(4), primary_key=True) | |
| amount = db.Column(db.Integer, nullable=False) | |
| send_amount = db.Column(db.Integer, nullable=False) | |
| claim_amount = db.Column(db.Integer, nullable=False) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow) | |
| verified = db.Column(db.Boolean, default=False) | |
| claimed = db.Column(db.Boolean, default=False) | |
| claimer_email = db.Column(db.String(255), nullable=True) | |
| def __repr__(self): | |
| return f'<ClaimLink {self.id}: {self.amount} Astras>' | |
| def get_transactions(): | |
| url = decrypt_value(ENCRYPTED_API_URL) | |
| referer = decrypt_value(ENCRYPTED_REFERER) | |
| connect_sid = decrypt_value(ENCRYPTED_CONNECT_SID) | |
| headers = { | |
| 'accept': '*/*', | |
| 'accept-language': 'en-US,en;q=0.9', | |
| 'cache-control': 'no-cache', | |
| 'pragma': 'no-cache', | |
| 'priority': 'u=1, i', | |
| 'referer': referer, | |
| 'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', | |
| 'sec-ch-ua-mobile': '?1', | |
| 'sec-ch-ua-platform': '"Android"', | |
| 'sec-fetch-dest': 'empty', | |
| 'sec-fetch-mode': 'cors', | |
| 'sec-fetch-site': 'same-origin', | |
| 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36' | |
| } | |
| cookies = { | |
| 'connect.sid': connect_sid | |
| } | |
| try: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Fetching transactions from API: {url}") | |
| response = requests.get(url, headers=headers, cookies=cookies, timeout=10) | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] API Response Status: {response.status_code}") | |
| transactions = response.json() | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] API Response: Received {len(transactions) if isinstance(transactions, list) else 'N/A'} transactions") | |
| if isinstance(transactions, list) and len(transactions) > 0: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Latest transaction: ID={transactions[0].get('id')}, Amount={transactions[0].get('amount')}, Type={transactions[0].get('transactionType')}, CreatedAt={transactions[0].get('createdAt')}") | |
| return transactions | |
| except Exception as e: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Error fetching transactions: {e}") | |
| return [] | |
| def check_for_payment(amount): | |
| try: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Checking for payment: Amount={amount}") | |
| transactions = get_transactions() | |
| current_time = time.time() | |
| one_minute_ago = current_time - 60 | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Checking transactions from last minute (since {datetime.fromtimestamp(one_minute_ago).strftime('%Y-%m-%d %H:%M:%S')})") | |
| matching_transactions = [] | |
| for transaction in transactions: | |
| if (transaction.get('amount') == amount and | |
| transaction.get('transactionType') == 'received'): | |
| matching_transactions.append(transaction) | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Found matching transaction: ID={transaction.get('id')}, Amount={transaction.get('amount')}, CreatedAt={transaction.get('createdAt')}") | |
| created_at_str = transaction.get('createdAt') | |
| if created_at_str: | |
| try: | |
| iso_str = created_at_str.replace('Z', '+00:00') | |
| created_at = datetime.fromisoformat(iso_str) | |
| created_at_timestamp = created_at.timestamp() | |
| age_seconds = current_time - created_at_timestamp | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Transaction age: {age_seconds:.2f} seconds") | |
| if created_at_timestamp >= one_minute_ago: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] ✓ Payment found! Transaction ID: {transaction.get('id')}") | |
| return transaction | |
| else: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Transaction too old (>{age_seconds:.2f}s), skipping") | |
| except Exception as e: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Error parsing timestamp: {e}") | |
| continue | |
| if matching_transactions: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Found {len(matching_transactions)} matching transaction(s) but none within last minute") | |
| else: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] No matching transactions found for amount {amount}") | |
| return None | |
| except Exception as e: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Error checking for payment: {e}") | |
| return None | |
| def get_user_uuid(email): | |
| try: | |
| base_url = decrypt_value(ENCRYPTED_BANK_BASE_URL) | |
| connect_sid = decrypt_value(ENCRYPTED_CONNECT_SID) | |
| referer = decrypt_value(ENCRYPTED_REFERER) | |
| url = f"{base_url}/api/user" | |
| params = {'email': email} | |
| headers = { | |
| 'accept': '*/*', | |
| 'accept-language': 'en-US,en;q=0.9', | |
| 'cache-control': 'no-cache', | |
| 'priority': 'u=1, i', | |
| 'referer': referer, | |
| 'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', | |
| 'sec-ch-ua-mobile': '?1', | |
| 'sec-ch-ua-platform': '"Android"', | |
| 'sec-fetch-dest': 'empty', | |
| 'sec-fetch-mode': 'cors', | |
| 'sec-fetch-site': 'same-origin', | |
| 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36' | |
| } | |
| cookies = { | |
| 'connect.sid': connect_sid | |
| } | |
| response = requests.get(url, params=params, headers=headers, cookies=cookies, timeout=10) | |
| if response.status_code == 200: | |
| return response.text.strip() | |
| else: | |
| print(f"Error getting user UUID: {response.status_code} - {response.text}") | |
| return None | |
| except Exception as e: | |
| print(f"Error getting user UUID: {e}") | |
| return None | |
| def send_money(to_user_id, amount, description): | |
| try: | |
| base_url = decrypt_value(ENCRYPTED_BANK_BASE_URL) | |
| connect_sid = decrypt_value(ENCRYPTED_CONNECT_SID) | |
| referer = decrypt_value(ENCRYPTED_REFERER) | |
| url = f"{base_url}/api/send-money" | |
| headers = { | |
| 'accept': '*/*', | |
| 'accept-language': 'en-US,en;q=0.9', | |
| 'cache-control': 'no-cache', | |
| 'content-type': 'application/json', | |
| 'origin': base_url, | |
| 'pragma': 'no-cache', | |
| 'priority': 'u=1, i', | |
| 'referer': referer, | |
| 'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', | |
| 'sec-ch-ua-mobile': '?1', | |
| 'sec-ch-ua-platform': '"Android"', | |
| 'sec-fetch-dest': 'empty', | |
| 'sec-fetch-mode': 'cors', | |
| 'sec-fetch-site': 'same-origin', | |
| 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36' | |
| } | |
| cookies = { | |
| 'connect.sid': connect_sid | |
| } | |
| data = { | |
| 'toUserId': to_user_id, | |
| 'amount': amount, | |
| 'description': description | |
| } | |
| response = requests.post(url, json=data, headers=headers, cookies=cookies, timeout=10) | |
| if response.status_code == 200: | |
| return True, response.json() | |
| else: | |
| print(f"Error sending money: {response.status_code} - {response.text}") | |
| return False, response.text | |
| except Exception as e: | |
| print(f"Error sending money: {e}") | |
| return False, str(e) | |
| with app.app_context(): | |
| db.create_all() | |
| def home(): | |
| return render_template('home.html') | |
| def create_payment_link(): | |
| try: | |
| data = request.get_json() | |
| if not data or 'amount' not in data or 'description' not in data or 'recipient_email' not in data: | |
| return jsonify({'error': 'Missing required fields'}), 400 | |
| amount = data['amount'] | |
| description = data['description'] | |
| recipient_email = data.get('recipient_email', '').strip() | |
| if not isinstance(amount, int) or amount <= 0: | |
| return jsonify({'error': 'Invalid amount'}), 400 | |
| if amount > 10000: | |
| return jsonify({'error': 'Maximum 10,000 Astras per payment link'}), 400 | |
| if not description or len(description.strip()) == 0: | |
| return jsonify({'error': 'Description cannot be empty'}), 400 | |
| if not recipient_email: | |
| return jsonify({'error': 'Recipient email is required'}), 400 | |
| payment_link = PaymentLink( | |
| id=generate_unique_code(), | |
| amount=amount, | |
| description=description.strip(), | |
| recipient_email=recipient_email | |
| ) | |
| db.session.add(payment_link) | |
| db.session.commit() | |
| payment_url = url_for('view_payment', payment_id=payment_link.id, _external=True) | |
| return jsonify({ | |
| 'success': True, | |
| 'payment_id': payment_link.id, | |
| 'payment_url': payment_url | |
| }) | |
| except Exception as e: | |
| db.session.rollback() | |
| return jsonify({'error': str(e)}), 500 | |
| def init_payment_check(payment_id): | |
| try: | |
| payment_link = PaymentLink.query.get_or_404(payment_id.upper()) | |
| fee = calculate_fee(payment_link.amount) | |
| total_amount = payment_link.amount + fee | |
| return jsonify({ | |
| 'amount': payment_link.amount, | |
| 'fee': fee, | |
| 'total_amount': total_amount | |
| }) | |
| except Exception as e: | |
| return jsonify({'error': str(e)}), 500 | |
| def check_payment_status(payment_id): | |
| try: | |
| payment_link = PaymentLink.query.get_or_404(payment_id.upper()) | |
| fee = calculate_fee(payment_link.amount) | |
| total_amount = payment_link.amount + fee | |
| payment_transaction = check_for_payment(total_amount) | |
| if payment_transaction: | |
| return jsonify({ | |
| 'payment_received': True, | |
| 'transaction': { | |
| 'id': payment_transaction.get('id'), | |
| 'amount': payment_transaction.get('amount'), | |
| 'from_email': payment_transaction.get('counterpartEmail'), | |
| 'description': payment_transaction.get('description') | |
| } | |
| }) | |
| else: | |
| return jsonify({'payment_received': False}) | |
| except Exception as e: | |
| return jsonify({'error': str(e)}), 500 | |
| def process_payment(payment_id): | |
| try: | |
| payment_link = PaymentLink.query.get_or_404(payment_id.upper()) | |
| if payment_link.paid: | |
| return jsonify({'error': 'Payment already completed'}), 400 | |
| payment_link.paid = True | |
| db.session.commit() | |
| if payment_link.recipient_email: | |
| try: | |
| fee = calculate_fee(payment_link.amount) | |
| amount_to_send = payment_link.amount - fee | |
| user_uuid = get_user_uuid(payment_link.recipient_email) | |
| if user_uuid: | |
| success, result = send_money( | |
| to_user_id=user_uuid, | |
| amount=amount_to_send, | |
| description=payment_link.description | |
| ) | |
| if success: | |
| print(f"Successfully sent {amount_to_send} Astras to {payment_link.recipient_email} (fee: {fee})") | |
| else: | |
| print(f"Failed to send money: {result}") | |
| else: | |
| print(f"Could not find user UUID for email: {payment_link.recipient_email}") | |
| except Exception as e: | |
| print(f"Error processing recipient payment: {e}") | |
| return jsonify({'success': True, 'message': 'Payment confirmed successfully'}) | |
| except Exception as e: | |
| db.session.rollback() | |
| return jsonify({'error': str(e)}), 500 | |
| def check_link(link_id): | |
| """Check if a link ID exists and return its type""" | |
| try: | |
| payment_link = PaymentLink.query.filter_by(id=link_id.upper()).first() | |
| if payment_link: | |
| return jsonify({ | |
| 'exists': True, | |
| 'type': 'payment' | |
| }) | |
| claim_link = ClaimLink.query.filter_by(id=link_id.upper()).first() | |
| if claim_link: | |
| return jsonify({ | |
| 'exists': True, | |
| 'type': 'claim' | |
| }) | |
| # Not found | |
| return jsonify({ | |
| 'exists': False | |
| }), 404 | |
| except Exception as e: | |
| return jsonify({'error': str(e)}), 500 | |
| def view_payment(payment_id): | |
| payment_link = PaymentLink.query.get_or_404(payment_id.upper()) | |
| fee = calculate_fee(payment_link.amount) | |
| total_amount = payment_link.amount + fee | |
| return render_template('payment.html', | |
| amount=payment_link.amount, | |
| fee=fee, | |
| total_amount=total_amount, | |
| description=payment_link.description, | |
| paid=payment_link.paid, | |
| payment_id=payment_link.id, | |
| recipient_email=payment_link.recipient_email) | |
| def bank(): | |
| return render_template('bank.html') | |
| def create_claim_link(): | |
| try: | |
| data = request.get_json() | |
| if not data or 'amount' not in data: | |
| return jsonify({'error': 'Missing required fields'}), 400 | |
| amount = data['amount'] | |
| if not isinstance(amount, int) or amount <= 0: | |
| return jsonify({'error': 'Invalid amount'}), 400 | |
| if amount > 10000: | |
| return jsonify({'error': 'Maximum 10,000 Astras per claim link'}), 400 | |
| send_amount, claim_amount = calculate_claim_amounts(amount) | |
| claim_link = ClaimLink( | |
| id=generate_unique_code(), | |
| amount=amount, | |
| send_amount=send_amount, | |
| claim_amount=claim_amount | |
| ) | |
| db.session.add(claim_link) | |
| db.session.commit() | |
| claim_url = url_for('view_claim', claim_id=claim_link.id, _external=True) | |
| return jsonify({ | |
| 'success': True, | |
| 'claim_id': claim_link.id, | |
| 'claim_url': claim_url, | |
| 'send_amount': send_amount, | |
| 'claim_amount': claim_amount | |
| }) | |
| except Exception as e: | |
| db.session.rollback() | |
| return jsonify({'error': str(e)}), 500 | |
| def view_claim(claim_id): | |
| claim_link = ClaimLink.query.get_or_404(claim_id.upper()) | |
| return render_template('claim.html', | |
| amount=claim_link.amount, | |
| send_amount=claim_link.send_amount, | |
| claim_amount=claim_link.claim_amount, | |
| verified=claim_link.verified, | |
| claimed=claim_link.claimed, | |
| claimer_email=claim_link.claimer_email, | |
| claim_id=claim_link.id) | |
| def check_claim_verification(claim_id): | |
| try: | |
| claim_link = ClaimLink.query.get_or_404(claim_id.upper()) | |
| if claim_link.verified: | |
| return jsonify({'verified': True}) | |
| payment_transaction = check_for_payment(claim_link.send_amount) | |
| if payment_transaction: | |
| claim_link.verified = True | |
| db.session.commit() | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Claim link {claim_id} verified") | |
| return jsonify({'verified': True}) | |
| else: | |
| return jsonify({'verified': False}) | |
| except Exception as e: | |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Error checking claim verification: {e}") | |
| return jsonify({'error': str(e)}), 500 | |
| def process_claim(claim_id): | |
| try: | |
| data = request.get_json() | |
| claim_link = ClaimLink.query.get_or_404(claim_id.upper()) | |
| if claim_link.claimed: | |
| return jsonify({'error': 'Claim link already used'}), 400 | |
| if not claim_link.verified: | |
| return jsonify({'error': 'Claim link not verified yet'}), 400 | |
| if not data or 'email' not in data: | |
| return jsonify({'error': 'Email is required'}), 400 | |
| email = data['email'].strip() | |
| if not email: | |
| return jsonify({'error': 'Email cannot be empty'}), 400 | |
| user_uuid = get_user_uuid(email) | |
| if not user_uuid: | |
| return jsonify({'error': 'User not found'}), 404 | |
| success, result = send_money( | |
| to_user_id=user_uuid, | |
| amount=claim_link.claim_amount, | |
| description=f'Claim link {claim_id}' | |
| ) | |
| if success: | |
| claim_link.claimed = True | |
| claim_link.claimer_email = email | |
| db.session.commit() | |
| return jsonify({'success': True, 'message': f'{claim_link.claim_amount} Astras sent to {email}'}) | |
| else: | |
| return jsonify({'error': f'Failed to send money: {result}'}), 500 | |
| except Exception as e: | |
| db.session.rollback() | |
| return jsonify({'error': str(e)}), 500 | |
| def handle_connect(): | |
| pass | |
| if __name__ == '__main__': | |
| thread = threading.Thread(target=background_stock_updates, daemon=True) | |
| thread.start() | |
| socketio.run(app, debug=True, host='0.0.0.0', port=7860, allow_unsafe_werkzeug=True) | |