| |
| """Set availability to 24/7 via undetected-chromedriver.""" |
| import time, os, json |
| import undetected_chromedriver as uc |
| from dotenv import load_dotenv |
| from selenium.webdriver.common.by import By |
| from selenium.webdriver.common.keys import Keys |
| from datetime import datetime, timezone |
|
|
| load_dotenv(os.path.join(os.path.dirname(__file__), ".env")) |
| load_dotenv() |
|
|
| USERNAME = os.getenv("RENTMASSEUR_USERNAME", "") |
| PASSWORD = os.getenv("RENTMASSEUR_PASSWORD", "") |
|
|
| options = uc.ChromeOptions() |
| options.add_argument("--window-size=1280,900") |
| options.add_argument("--disable-blink-features=AutomationControlled") |
|
|
| driver = uc.Chrome(options=options, version_main=149) |
|
|
| try: |
| print("[1] Login...") |
| driver.get("https://rentmasseur.com/login") |
| time.sleep(6) |
| driver.execute_script(""" |
| const pwd = document.querySelector('input[type="password"]'); |
| const user = document.querySelector('input[type="text"], input[type="email"]'); |
| const ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; |
| if (user) { ns.call(user, arguments[0]); user.dispatchEvent(new Event('input', {bubbles: true})); } |
| if (pwd) { ns.call(pwd, arguments[1]); pwd.dispatchEvent(new Event('input', {bubbles: true})); } |
| """, USERNAME, PASSWORD) |
| time.sleep(1) |
| driver.find_element(By.CSS_SELECTOR, 'input[type="password"]').send_keys(Keys.ENTER) |
| time.sleep(5) |
| print(f" URL: {driver.current_url}") |
| |
| print("\n[2] Setting availability...") |
| driver.get("https://rentmasseur.com/settings/travels") |
| time.sleep(5) |
| |
| |
| info = driver.execute_script(""" |
| const r = {selects: [], buttons: [], inputs: [], textareas: []}; |
| document.querySelectorAll('select').forEach(s => { |
| r.selects.push({id: s.id, name: s.name, options: Array.from(s.options).map(o => o.text).join('|'), value: s.value}); |
| }); |
| document.querySelectorAll('button').forEach(b => r.buttons.push({text: b.innerText.trim(), type: b.type, id: b.id})); |
| document.querySelectorAll('input').forEach(i => r.inputs.push({type: i.type, id: i.id, name: i.name, value: (i.value||'').substring(0,30), checked: i.checked})); |
| document.querySelectorAll('textarea').forEach(t => r.textareas.push({id: t.id, name: t.name, value: (t.value||'').substring(0,50)})); |
| return r; |
| """) |
| |
| print(f" Selects: {len(info['selects'])}") |
| for s in info['selects']: |
| print(f" id={s['id']} name={s['name']} value={s['value']} options={s['options'][:80]}") |
| print(f" Buttons: {len(info['buttons'])}") |
| for b in info['buttons']: |
| print(f" {b['text']} type={b['type']}") |
| print(f" Inputs: {len(info['inputs'])}") |
| for i in info['inputs']: |
| print(f" type={i['type']} id={i['id']} name={i['name']} value={i['value']} checked={i['checked']}") |
| |
| |
| set_result = driver.execute_script(""" |
| // Look for availability select or radio |
| const selects = Array.from(document.querySelectorAll('select')); |
| const availSelect = selects.find(s => { |
| const opts = Array.from(s.options).map(o => o.text.toLowerCase()); |
| return opts.some(o => o.includes('available')); |
| }); |
| |
| if (availSelect) { |
| const availOpt = Array.from(availSelect.options).find(o => |
| o.text.toLowerCase().includes('available') && !o.text.toLowerCase().includes('not')); |
| if (availOpt) { |
| availSelect.value = availOpt.value; |
| availSelect.dispatchEvent(new Event('change', {bubbles: true})); |
| return {method: 'select', value: availOpt.text}; |
| } |
| } |
| |
| // Look for radio buttons |
| const radios = Array.from(document.querySelectorAll('input[type="radio"]')); |
| const availRadio = radios.find(r => /avail/i.test(r.name + r.id + (r.value||''))); |
| if (availRadio) { |
| availRadio.click(); |
| return {method: 'radio', value: availRadio.value}; |
| } |
| |
| // Look for toggle/checkbox |
| const checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]')); |
| const availCheckbox = checkboxes.find(c => /avail/i.test(c.name + c.id)); |
| if (availCheckbox && !availCheckbox.checked) { |
| availCheckbox.click(); |
| return {method: 'checkbox', checked: true}; |
| } |
| |
| return {error: 'no_availability_control'}; |
| """) |
| print(f"\n Set result: {set_result}") |
| |
| if 'error' not in set_result: |
| time.sleep(1) |
| |
| saved = driver.execute_script(""" |
| const btns = Array.from(document.querySelectorAll('button, input[type="submit"]')); |
| const sb = btns.find(b => /save|update|submit|apply|confirm/i.test((b.innerText||'')+' '+(b.value||''))); |
| if (sb) { sb.click(); return {ok: true, text: sb.innerText}; } |
| return {ok: false}; |
| """) |
| print(f" Save: {saved}") |
| time.sleep(3) |
| |
| |
| receipts_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "receipts") |
| os.makedirs(receipts_dir, exist_ok=True) |
| receipt = {"action": "set_availability", "status": "available_24_7", "success": True, "timestamp": datetime.now(timezone.utc).isoformat(), "method": "undetected-chromedriver"} |
| rpath = os.path.join(receipts_dir, f"availability_{receipt['timestamp'].replace(':','-')}.json") |
| with open(rpath, "w") as f: |
| json.dump(receipt, f, indent=2) |
| print(f"\n=== AVAILABILITY SET === Receipt: {rpath}") |
| else: |
| |
| print(" Trying /settings page directly...") |
| driver.get("https://rentmasseur.com/settings") |
| time.sleep(4) |
| driver.save_screenshot("/Users/alep/Downloads/rentmasseur-optimizer/settings_avail.png") |
| |
| |
| toggles = driver.execute_script(""" |
| return Array.from(document.querySelectorAll('input[type="checkbox"], .toggle, .switch, [role="switch"]')).map(e => ({ |
| type: e.type, id: e.id, name: e.name, checked: e.checked, |
| label: e.closest('label') ? e.closest('label').innerText : (e.parentElement ? e.parentElement.innerText : '') |
| })); |
| """) |
| print(f" Toggles: {len(toggles)}") |
| for t in toggles: |
| print(f" {t}") |
|
|
| finally: |
| driver.quit() |
| print("\nDone.") |
|
|