I want to update the
-
Auto Login with Multiple Sessions:
- Add an “Auto Login” button.
- Add a box to input how many sessions (browser windows) should be logged in.
- The script should automatically log in to multiple browser sessions using the same credentials.
- This will allow faster purchases by using multiple sessions in parallel.
-
Product URL Input and Live Product Fetch:
- Add a box to input the product page URL.
- Add a “Fetch Products” button that opens the product page (without login) and fetches all live products from that URL.
- Display all available products on the page along with their stock status.
-
Wait Before Checkout:
- When the product page opens in the browser, add a 1–2 second delay before clicking the checkout button.
- This is to avoid accidental purchase of a previously selected product that might still be in the checkout cart.
-
Product Selection and Quantity Handling:
- From interface, allow the user to select a product and enter the quantity they want to purchase.
- Based on the selected quantity, the script should open the product in multiple tabs across available sessions and proceed to checkout for each one.
- All operations should happen using the already logged-in sessions.
...
from flask import Flask, request, jsonify, render_template_string
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
import pyotp
import time
import threading
import json
from flask_cors import CORS
app = Flask(name)
CORS(app)
--- Global State Management for the Selenium Driver ---
This dictionary will hold the driver instance after a successful login.
driver_state = {
'driver': None,
'wait': None,
'session_active': False,
'totp_key': None,
'base_url': "https://gold.razer.com/global/en/gold/catalog/pubg-mobile-uc-code" # Default
}
--- Constants ---
RAZER_LOGIN_URL = "https://razerid.razer.com/?client_id=63c74d17e027dc11f642146bfeeaee09c3ce23d8&redirect=https%3A%2F%2Fgold.razer.com%2Faccount%2Flogin"
--- Helper Function to Initialize WebDriver ---
def init_webdriver(headless=False):
"""Initializes and returns a Chrome WebDriver instance."""
chrome_options = Options()
chrome_options.add_argument("--disable-extensions")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--window-size=1920,1080")
if headless:
chrome_options.add_argument("--headless")
chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
else:
chrome_options.add_argument("--start-maximized")
chrome_options.page_load_strategy = 'normal'
try:
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
return driver
except Exception as e:
print(f"Error initializing WebDriver: {e}")
return None
--- Helper Function to Fetch Live Product Data (for /get_products endpoint) ---
def fetch_product_denominations(product_url):
"""
Fetches live product denomination and stock status from the Razer Gold page
using a dedicated headless driver instance.
"""
driver = None
MAX_WAIT_TIME = 30
try:
driver = init_webdriver(headless=True)
if not driver:
return {"status": "error", "message": "Failed to initialize Chrome Driver for scraping."}
wait = WebDriverWait(driver, MAX_WAIT_TIME)
driver.get(product_url)
PRODUCT_LABEL_XPATH = "//div[@id='webshop_step_sku']//div[contains(@class, 'selection-tile')]/label"
wait.until(EC.presence_of_all_elements_located((By.XPATH, PRODUCT_LABEL_XPATH)))
labels = driver.find_elements(By.XPATH, PRODUCT_LABEL_XPATH)
product_list = []
for label in labels:
try:
text_div = label.find_element(By.XPATH, ".//div[contains(@class, 'selection-tile__text')]")
uc_text = text_div.text.replace('PUBG', '').replace('UC', '').strip()
denomination = int(uc_text)
is_out_of_stock = False
try:
label.find_element(By.XPATH, ".//span[contains(text(), 'Out of stock')]")
is_out_of_stock = True
except:
pass
price_text = f"USD {round(denomination / 60, 2)}"
product_list.append({
"denomination": denomination,
"price": price_text,
"out_of_stock": is_out_of_stock
})
except Exception as e:
# print(f"Skipping product tile due to error in parsing: {e}")
continue
valid_products = [p for p in product_list if p['denomination'] > 0]
print(f"--- Successfully Fetched Products ({len(valid_products)} items) ---: {valid_products}")
return {"status": "success", "products": valid_products}
except Exception as e:
print(f"\n--- CRITICAL ERROR in fetch_product_denominations ---")
print(f"Error: Failed to fetch products, likely due to timeout or element change. Message: {e}\n")
return {"status": "error", "message": f"Failed to fetch products: {e}"}
finally:
try:
if driver:
driver.quit()
except:
pass
--- Automation Logic: Login Function ---
def start_razer_login(email, password, totp_key, product_url):
"""Performs login and opens the product page, then stores the driver in global state."""
# Clean up previous session if it exists
if driver_state['driver']:
try:
driver_state['driver'].quit()
except:
pass
driver_state['driver'] = None
driver_state['wait'] = None
driver_state['session_active'] = False
driver = init_webdriver(headless=False)
if not driver:
return {"status": "Error", "message": "Could not start Chrome Driver."}
wait = WebDriverWait(driver, 60)
# Update global state with the new driver and other details
driver_state['driver'] = driver
driver_state['wait'] = wait
driver_state['totp_key'] = totp_key
driver_state['base_url'] = product_url
try:
# --- A. Login Phase ---
print("Starting Login...")
driver.get(RAZER_LOGIN_URL)
email_field = wait.until(EC.presence_of_element_located((By.ID, "input-login-email")))
email_field.send_keys(email)
driver.find_element(By.ID, "input-login-password").send_keys(password)
driver.find_element(By.ID, "btn-log-in").click()
print("Login button clicked. Waiting 10 seconds for dashboard to load (Increased timeout)...")
time.sleep(10) # Increased sleep time for stability
# --- B. Navigate to Product Page ---
driver.get(product_url)
print("Redirected to product page. Session is now ready for purchase.")
driver_state['session_active'] = True
# NOTE: Returning status here so the Flask thread can send an immediate response.
return {"status": "Success", "message": "Login successful. Driver is active and on the product page. You can now perform purchases from the web UI."}
except Exception as e:
print(f"An unexpected error occurred during login: {e}")
try:
driver.quit()
except:
pass
driver_state['driver'] = None
driver_state['wait'] = None
driver_state['session_active'] = False
return {"status": "Error", "message": f"Login automation failed. An error occurred: {e}. Check CMD for details."}
--- Automation Logic: Purchase and 2FA (Unchanged) ---
def handle_2fa_input(driver, totp_key):
"""Handles 2FA using JavaScript input and a FRESH TOTP code."""
wait = WebDriverWait(driver, 30)
IFRAME_XPATH = "//iframe[contains(@title, 'Razer OTP') or contains(@src, 'otp?') or @id='otp-iframe-1']"
try:
# 1. Generate FRESH TOTP CODE
otp_code = None
try:
totp = pyotp.TOTP(totp_key)
otp_code = totp.now()
except:
return False
if not otp_code or len(otp_code) != 6:
return False
# 2. Wait for and switch to the specific IFRAME
iframe_element = wait.until(EC.presence_of_element_located((By.XPATH, IFRAME_XPATH)))
driver.switch_to.frame(iframe_element)
# 3. Wait for the first NEW input box
NEW_INPUT_ID = "otp-input-0"
wait.until(EC.presence_of_element_located((By.ID, NEW_INPUT_ID)))
# 4. Input each digit using JavaScript
for i, digit in enumerate(otp_code):
input_id = f"otp-input-{i}"
try:
input_element = driver.find_element(By.ID, input_id)
driver.execute_script("arguments[0].value = arguments[1];", input_element, digit)
driver.execute_script("arguments[0].dispatchEvent(new Event('input', { bubbles: true }));", input_element)
except:
return False
return True
except Exception as e:
print(f"2FA Input Critical Failure: {e}")
return False
finally:
try:
driver.switch_to.default_content()
except:
pass
def perform_purchase_action(driver, wait, product_link, denomination, totp_key):
"""Performs the single purchase action."""
try:
driver.get(product_link)
# --- STEP 1: Select Product (UC Denomination) ---
full_uc_text = f"PUBG {denomination} UC"
uc_label_xpath = f"//div[@id='webshop_step_sku']//div[contains(text(),'{full_uc_text}')]/ancestor::label[1]"
label_element = wait.until(EC.element_to_be_clickable((By.XPATH, uc_label_xpath)))
driver.execute_script("arguments[0].click();", label_element)
# --- STEP 2: Select Payment Method (Razer Gold) ---
razer_gold_xpath = "//div[contains(@class, 'payment-channels-list')]//div[contains(text(), 'Razer Gold')]/ancestor::label[1]"
razer_gold_label = wait.until(EC.element_to_be_clickable((By.XPATH, razer_gold_xpath)))
driver.execute_script("arguments[0].click();", razer_gold_label)
# --- STEP 3: Click Checkout ---
checkout_button_xpath = "//button[@data-cs-override-id='purchase-webshop-checkout-btn']"
checkout_button = wait.until(EC.element_to_be_clickable((By.XPATH, checkout_button_xpath)))
checkout_button.click()
# --- STEP 4: HANDLE 2FA / FINAL STEP (Pass TOTP key) ---
handle_2fa_input(driver, totp_key)
return True
except Exception as e:
print(f"ERROR processing {denomination} UC: Purchase automation failed. Message: {e}")
return False
def _process_purchases_threaded(products_to_buy, product_link):
"""Handles the purchase logic in a separate thread."""
driver = driver_state['driver']
wait = driver_state['wait']
totp_key = driver_state['totp_key']
product_count = 0
total_items = sum(p['quantity'] for p in products_to_buy)
if not driver or not wait or not totp_key:
print("Thread Error: Driver or essential data missing. Purchase aborted.")
return
try:
driver.switch_to.window(driver.window_handles[0])
for product in products_to_buy:
denomination = str(product['denomination'])
quantity = product['quantity']
for i in range(quantity):
if product_count > 0:
driver.execute_script("window.open('');")
driver.switch_to.window(driver.window_handles[-1])
print(f"Processing: {denomination} UC (Item {product_count+1}/{total_items})")
if perform_purchase_action(driver, wait, product_link, denomination, totp_key):
product_count += 1
else:
try:
driver.close()
driver.switch_to.window(driver.window_handles[0])
except:
pass
if product_count > 0:
driver.switch_to.window(driver.window_handles[0])
print(f"--- Purchase Phase Complete ({product_count} successful purchases) ---")
except Exception as e:
print(f"CRITICAL ERROR during purchase phase: {e}")
driver_state['session_active'] = False
print(f"Purchase Thread Finished. Total successful: {product_count}")
--- Flask Server Setup (Updated HTML Template) ---
HTML_FORM_TEMPLATE = """
<!doctype html>
<title>Razer Gold Automation</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background-color: #f4f4f4; }
.container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); max-width: 800px; margin: auto; }
h1 { color: #008000; }
label { display: block; margin-top: 10px; font-weight: bold; }
input[type="text"], input[type="password"], textarea { width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
button { background-color: #008000; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; margin-top: 20px; margin-right: 10px; }
button:hover { background-color: #006400; }
#message { margin-top: 20px; padding: 10px; border-radius: 4px; }
.error { background-color: #fdd; border: 1px solid #f00; color: #f00; }
.success { background-color: #dfd; border: 1px solid #0f0; color: #008000; }
.info { background-color: #e0f2f7; border: 1px solid #b3e5fc; color: #0288d1; }
.note { margin-top: 15px; padding: 10px; background-color: #ffffe0; border: 1px solid #cccc00; border-radius: 4px;}
.product-list-container { margin-top: 20px; border: 1px solid #ccc; padding: 15px; border-radius: 4px; background-color: #fafafa; }
.product-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding-bottom: 5px; border-bottom: 1px dashed #eee; }
.product-item:last-child { border-bottom: none; margin-bottom: 0; }
.product-info { font-weight: 500; }
.product-input input { width: 60px; text-align: center; margin-left: 10px; }
.out-of-stock { color: #ff0000; font-weight: bold; }
.disabled-item { background-color: #f8f8f8; opacity: 0.6; }
.button-group { display: flex; gap: 10px; margin-top: 10px; }
.purchase-group { margin-top: 25px; border-top: 2px solid #ccc; padding-top: 15px; }
</style>
Razer Gold Automation Control
<div id="status-display" class="info">
Driver Status: Not Active.
</div>
<form id="controlForm">
<label for="product_url">Product Page URL:</label>
<input type="text" id="product_url" name="product_url" value="https://gold.razer.com/global/en/gold/catalog/pubg-mobile-uc-code" required>
<button type="button" onclick="fetchProducts()">Refresh / Fetch Products</button>
<div class="note">
<strong>STEP 1: LOGIN (Required for Purchase)</strong>
</div>
<label for="email">Razer ID Email:</label>
<input type="text" id="email" name="email" required>
<label for="password">Razer ID Password:</label>
<input type="password" id="password" name="password" required>
<label for="totp_key">TOTP Secret Key (Base32):</label>
<input type="text" id="totp_key" name="totp_key" required>
<button type="button" onclick="startLogin()">Start Auto Login</button>
<div class="purchase-group">
<div class="note">
<strong>STEP 2: PURCHASE (Requires successful login)</strong><br>
Items marked <span class="out-of-stock">RED</span> are Out of Stock and will be skipped by the script.
</div>
<label>Select Quantities to Purchase:</label>
<div class="product-list-container">
<div id="product-list-area">Click 'Refresh / Fetch Products' to load data.</div>
</div>
<button type="button" onclick="startPurchase()">Perform Purchases</button>
</div>
</form>
<div id="message"></div>
<script>
let availableProducts = [];
function updateStatus(status) {
const statusDiv = document.getElementById('status-display');
statusDiv.className = status.includes('Active') ? 'success' : 'info';
if (status.includes('Error')) {
statusDiv.className = 'error';
}
statusDiv.innerHTML = '
Driver Status: ' + status;
}
// Function to fetch product list from the new backend endpoint
function fetchProducts() {
const listArea = document.getElementById('product-list-area');
const productUrl = document.getElementById('product_url').value;
listArea.innerHTML = 'Fetching products... This may take a few seconds as a headless browser is launched.';
document.getElementById('message').textContent = '';
fetch('/get_products', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ product_url: productUrl })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success' && data.products.length > 0) {
availableProducts = data.products;
renderProductList(data.products);
document.getElementById('message').textContent = 'Product list successfully refreshed.';
document.getElementById('message').className = 'success';
} else {
const errorMessage = data.message || 'Please check the backend console for the exact error.';
listArea.innerHTML = `
Error: Could not fetch products. ${errorMessage}`;
document.getElementById('message').textContent = `Product fetch failed: ${errorMessage}`;
document.getElementById('message').className = 'error';
}
})
.catch(error => {
listArea.innerHTML = `
Network Error: ${error}. Ensure Python server is running.`;
document.getElementById('message').textContent = `Network Error during product fetch: ${error}`;
document.getElementById('message').className = 'error';
});
}
// Function to dynamically create the list of products on the HTML
function renderProductList(products) {
const listArea = document.getElementById('product-list-area');
listArea.innerHTML = '';
products.forEach((product, index) => {
const itemDiv = document.createElement('div');
itemDiv.className = 'product-item' + (product.out_of_stock ? '' : '');
const infoDiv = document.createElement('div');
infoDiv.className = 'product-info';
let infoText = `PUBG ${product.denomination} UC (${product.price})`;
if (product.out_of_stock) {
infoText += `
[OUT OF STOCK]`;
}
infoDiv.innerHTML = infoText;
const inputDiv = document.createElement('div');
inputDiv.className = 'product-input';
const inputField = document.createElement('input');
inputField.type = 'number';
inputField.min = '0';
inputField.value = '0';
inputField.id = `qty-${index}`;
inputField.setAttribute('data-denomination', product.denomination);
inputDiv.appendChild(inputField);
itemDiv.appendChild(infoDiv);
itemDiv.appendChild(inputDiv);
listArea.appendChild(itemDiv);
});
}
// Function for Auto Login button
function startLogin() {
const messageDiv = document.getElementById('message');
if (!document.getElementById('email').value || !document.getElementById('password').value || !document.getElementById('totp_key').value || !document.getElementById('product_url').value) {
messageDiv.textContent = 'Error: All login fields and Product URL are required for Auto Login.';
messageDiv.className = 'error';
return;
}
messageDiv.textContent = 'Starting Auto Login... Please wait for a new Chrome window to open...';
messageDiv.className = 'info';
updateStatus('Connecting...');
const loginData = {
email: document.getElementById('email').value,
password: document.getElementById('password').value,
totp_key: document.getElementById('totp_key').value,
product_url: document.getElementById('product_url').value
};
fetch('/start_login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(loginData)
})
.then(response => response.json())
.then(result => {
messageDiv.textContent = result.message;
messageDiv.className = result.status === 'Error' ? 'error' : 'success';
// Driver Status update for login is now simpler, relying on the success message.
// If the console says success, but the front-end says error, the user can still proceed.
updateStatus(result.status === 'Success' ? 'Active and Logged In' : 'Error / Check Console');
})
.catch(error => {
messageDiv.textContent = 'Network Error during login: ' + error;
messageDiv.className = 'error';
updateStatus('Error / Not Active');
});
}
// Function for Perform Purchase button
function startPurchase() {
const messageDiv = document.getElementById('message');
// *** IMPORTANT FIX: Removed the front-end status check ***
// We trust the Python console output you provided. Backend check remains.
const productsToBuy = [];
let totalQuantity = 0;
// Iterate through all product quantity inputs
availableProducts.forEach((product, index) => {
const inputField = document.getElementById(`qty-${index}`);
const quantity = parseInt(inputField.value);
// Only include in-stock items with quantity > 0
if (quantity > 0 && !product.out_of_stock) {
productsToBuy.push({
denomination: product.denomination,
quantity: quantity
});
totalQuantity += quantity;
}
});
if (totalQuantity === 0) {
messageDiv.textContent = 'Error: Please select at least one IN-STOCK item to purchase.';
messageDiv.className = 'error';
return;
}
messageDiv.textContent = `Starting purchase for ${totalQuantity} items...`;
messageDiv.className = 'info';
updateStatus('Active - Running Purchase...');
const purchaseData = {
products: productsToBuy,
product_link: document.getElementById('product_url').value
};
fetch('/perform_purchase', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(purchaseData)
})
.then(response => response.json())
.then(result => {
messageDiv.textContent = result.message;
messageDiv.className = result.status === 'Error' ? 'error' : 'success';
// Revert status to logged-in or error based on the purchase result
updateStatus(result.status === 'Success' ? 'Active and Logged In (Finished Purchase)' : 'Error / Check Console');
})
.catch(error => {
messageDiv.textContent = 'Network Error during purchase: ' + error;
messageDiv.className = 'error';
updateStatus('Error / Not Active');
});
}
window.onload = function() {
updateStatus('Not Active');
fetchProducts();
};
</script>
"""
@app.route('/', methods=['GET'])
def index():
return render_template_string(HTML_FORM_TEMPLATE)
@app.route('/get_products', methods=['POST'])
def get_products():
"""Endpoint to fetch and return live product list from the provided URL."""
data = request.json
product_url = data.get('product_url', driver_state['base_url'])
result = fetch_product_denominations(product_url)
return jsonify(result)
@app.route('/start_login', methods=['POST'])
def handle_login_request():
"""Endpoint to start the login process and keep the driver open."""
data = request.json
email = data.get('email')
password = data.get('password')
totp_key = data.get('totp_key')
product_url = data.get('product_url', driver_state['base_url'])
if not all([email, password, totp_key, product_url]):
return jsonify({"status": "Error", "message": "Missing email, password, TOTP key, or product URL."}), 400
# Start the login in a separate thread
# We create a function to manage the thread and return its result for the Flask response
def run_login_and_report():
result = start_razer_login(email, password, totp_key, product_url)
# In a real app, this would use websockets to push status to the front-end.
# Here, we just rely on the main function to start the thread.
print(f"Login Thread Status Report: {result['status']} - {result['message']}")
threading.Thread(target=run_login_and_report).start()
return jsonify({
"status": "Initiated",
"message": "Login script started in the background. Please wait for the Chrome window to open. The Backend session is ready if the console shows success."
})
@app.route('/perform_purchase', methods=['POST'])
def handle_purchase_request():
"""Endpoint to perform the purchase using the existing, logged-in driver."""
data = request.json
products_to_buy = data.get('products')
product_link = data.get('product_link')
# This is the critical BACKEND check.
if not driver_state['session_active'] or not driver_state['driver']:
return jsonify({"status": "Error", "message": "No active session found in the backend. Please restart 'Start Auto Login'."}), 400
if not products_to_buy or not product_link:
return jsonify({"status": "Error", "message": "Missing product data or product link."}), 400
threading.Thread(target=_process_purchases_threaded, args=(products_to_buy, product_link)).start()
return jsonify({
"status": "Initiated",
"message": "Purchase script started in the background. New tabs will open in the existing Chrome window. Monitor the console for 2FA messages."
})
if name == 'main':
driver_state['base_url'] = "https://gold.razer.com/global/en/gold/catalog/pubg-mobile-uc-code"
print("Starting Flask Server... Access http://127.0.0.1:5000 in your browser.")
app.run(host='127.0.0.1', port=5000)
I want to update the
Auto Login with Multiple Sessions:
- Add an “Auto Login” button.
- Add a box to input how many sessions (browser windows) should be logged in.
- The script should automatically log in to multiple browser sessions using the same credentials.
- This will allow faster purchases by using multiple sessions in parallel.
Product URL Input and Live Product Fetch:
- Add a box to input the product page URL.
- Add a “Fetch Products” button that opens the product page (without login) and fetches all live products from that URL.
- Display all available products on the page along with their stock status.
Wait Before Checkout:
- When the product page opens in the browser, add a 1–2 second delay before clicking the checkout button.
- This is to avoid accidental purchase of a previously selected product that might still be in the checkout cart.
Product Selection and Quantity Handling:
- From interface, allow the user to select a product and enter the quantity they want to purchase.
- All operations should happen using the already logged-in sessions.
...
from flask import Flask, request, jsonify, render_template_string
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
import pyotp
import time
import threading
import json
from flask_cors import CORS
app = Flask(name)
CORS(app)
--- Global State Management for the Selenium Driver ---
This dictionary will hold the driver instance after a successful login.
driver_state = {
'driver': None,
'wait': None,
'session_active': False,
'totp_key': None,
'base_url': "https://gold.razer.com/global/en/gold/catalog/pubg-mobile-uc-code" # Default
}
--- Constants ---
RAZER_LOGIN_URL = "https://razerid.razer.com/?client_id=63c74d17e027dc11f642146bfeeaee09c3ce23d8&redirect=https%3A%2F%2Fgold.razer.com%2Faccount%2Flogin"
--- Helper Function to Initialize WebDriver ---
def init_webdriver(headless=False):
"""Initializes and returns a Chrome WebDriver instance."""
chrome_options = Options()
chrome_options.add_argument("--disable-extensions")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--window-size=1920,1080")
if headless:
chrome_options.add_argument("--headless")
chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
else:
chrome_options.add_argument("--start-maximized")
--- Helper Function to Fetch Live Product Data (for /get_products endpoint) ---
def fetch_product_denominations(product_url):
"""
Fetches live product denomination and stock status from the Razer Gold page
using a dedicated headless driver instance.
"""
driver = None
MAX_WAIT_TIME = 30
--- Automation Logic: Login Function ---
def start_razer_login(email, password, totp_key, product_url):
"""Performs login and opens the product page, then stores the driver in global state."""
--- Automation Logic: Purchase and 2FA (Unchanged) ---
def handle_2fa_input(driver, totp_key):
"""Handles 2FA using JavaScript input and a FRESH TOTP code."""
wait = WebDriverWait(driver, 30)
IFRAME_XPATH = "//iframe[contains(@title, 'Razer OTP') or contains(@src, 'otp?') or @id='otp-iframe-1']"
def perform_purchase_action(driver, wait, product_link, denomination, totp_key):
"""Performs the single purchase action."""
try:
driver.get(product_link)
def _process_purchases_threaded(products_to_buy, product_link):
"""Handles the purchase logic in a separate thread."""
driver = driver_state['driver']
wait = driver_state['wait']
totp_key = driver_state['totp_key']
--- Flask Server Setup (Updated HTML Template) ---
HTML_FORM_TEMPLATE = """
<title>Razer Gold Automation</title> <style> body { font-family: Arial, sans-serif; padding: 20px; background-color: #f4f4f4; } .container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); max-width: 800px; margin: auto; } h1 { color: #008000; } label { display: block; margin-top: 10px; font-weight: bold; } input[type="text"], input[type="password"], textarea { width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { background-color: #008000; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; margin-top: 20px; margin-right: 10px; } button:hover { background-color: #006400; } #message { margin-top: 20px; padding: 10px; border-radius: 4px; } .error { background-color: #fdd; border: 1px solid #f00; color: #f00; } .success { background-color: #dfd; border: 1px solid #0f0; color: #008000; } .info { background-color: #e0f2f7; border: 1px solid #b3e5fc; color: #0288d1; } .note { margin-top: 15px; padding: 10px; background-color: #ffffe0; border: 1px solid #cccc00; border-radius: 4px;} .product-list-container { margin-top: 20px; border: 1px solid #ccc; padding: 15px; border-radius: 4px; background-color: #fafafa; } .product-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding-bottom: 5px; border-bottom: 1px dashed #eee; } .product-item:last-child { border-bottom: none; margin-bottom: 0; } .product-info { font-weight: 500; } .product-input input { width: 60px; text-align: center; margin-left: 10px; } .out-of-stock { color: #ff0000; font-weight: bold; } .disabled-item { background-color: #f8f8f8; opacity: 0.6; } .button-group { display: flex; gap: 10px; margin-top: 10px; } .purchase-group { margin-top: 25px; border-top: 2px solid #ccc; padding-top: 15px; } </style><!doctype html>
Razer Gold Automation Control
@app.route('/', methods=['GET'])
def index():
return render_template_string(HTML_FORM_TEMPLATE)
@app.route('/get_products', methods=['POST'])
def get_products():
"""Endpoint to fetch and return live product list from the provided URL."""
data = request.json
product_url = data.get('product_url', driver_state['base_url'])
result = fetch_product_denominations(product_url)
return jsonify(result)
@app.route('/start_login', methods=['POST'])
def handle_login_request():
"""Endpoint to start the login process and keep the driver open."""
data = request.json
email = data.get('email')
password = data.get('password')
totp_key = data.get('totp_key')
product_url = data.get('product_url', driver_state['base_url'])
@app.route('/perform_purchase', methods=['POST'])
def handle_purchase_request():
"""Endpoint to perform the purchase using the existing, logged-in driver."""
data = request.json
products_to_buy = data.get('products')
product_link = data.get('product_link')
if name == 'main':
driver_state['base_url'] = "https://gold.razer.com/global/en/gold/catalog/pubg-mobile-uc-code"