diff --git a/chromedriver.exe b/chromedriver.exe new file mode 100644 index 00000000..6306915f Binary files /dev/null and b/chromedriver.exe differ diff --git a/visa.py b/visa.py index 299eb5be..4c964da1 100644 --- a/visa.py +++ b/visa.py @@ -14,6 +14,9 @@ from selenium.webdriver.support.ui import WebDriverWait as Wait from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.support.select import Select from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail @@ -36,14 +39,14 @@ LOCAL_USE = config['CHROMEDRIVER'].getboolean('LOCAL_USE') HUB_ADDRESS = config['CHROMEDRIVER']['HUB_ADDRESS'] -REGEX_CONTINUE = "//a[contains(text(),'Continuar')]" +REGEX_CONTINUE = "//a[contains(text(),'Continue')]" # def MY_CONDITION(month, day): return int(month) == 11 and int(day) >= 5 def MY_CONDITION(month, day): return True # No custom condition wanted for the new scheduled date STEP_TIME = 0.5 # time between steps (interactions with forms): 0.5 seconds -RETRY_TIME = 60*10 # wait time between retries/checks for available dates: 10 minutes +RETRY_TIME = 60*3 # wait time between retries/checks for available dates: 3 minutes EXCEPTION_TIME = 60*30 # wait time when an exception occurs: 30 minutes COOLDOWN_TIME = 60*60 # wait time when temporary banned (empty list): 60 minutes @@ -83,7 +86,12 @@ def send_notification(msg): def get_driver(): if LOCAL_USE: - dr = webdriver.Chrome(service=Service(ChromeDriverManager().install())) + caps = DesiredCapabilities.CHROME + caps['goog:loggingPrefs'] = {'performance': 'ALL'} + # dr = webdriver.Chrome(desired_capabilities=caps, service=Service(ChromeDriverManager().install())) + service = Service() + options = webdriver.ChromeOptions() + dr = webdriver.Chrome(service=service, options=options) else: dr = webdriver.Remote(command_executor=HUB_ADDRESS, options=webdriver.ChromeOptions()) return dr @@ -91,6 +99,51 @@ def get_driver(): driver = get_driver() + +def process_browser_log_entry(entry): + response = json.loads(entry['message'])['message'] + return response + + + +def get_date_new(url): + # no longer able to jump with url, go there by clicking the button + #driver.get(url) + continueBtn = driver.find_element(By.XPATH, '//a[contains(text(),"Continue")]') + continueBtn.click() + time.sleep(2) # wait for all the data to arrive. + # find the 4th item's child element in the list + serviceList = driver.find_element(By.XPATH, '//ul[@class="accordion custom_icons"]') + child_elements = serviceList.find_elements_by_css_selector("li") + # get the 4th chiild item from the list then the first item that + rescheduleBtn = (child_elements[3].find_elements_by_css_selector("a"))[0] + rescheduleBtn.click() + time.sleep(2) # wait for all the data to arrive. + realRescheduleBtn = driver.find_element(By.XPATH, '//a[contains(text(),"Reschedule Appointment")]') + realRescheduleBtn.click() + time.sleep(2) # wait for all the data to arrive. + browser_log = driver.get_log('performance') + events = [process_browser_log_entry(entry) for entry in browser_log] + events = [event for event in events if 'Network.response' in event['method']] + targetIndex = -1; + for event in events: + if "response" in event["params"] and "url" in event["params"]["response"]: + if "/appointment/days/" in event["params"]["response"]["url"]: + print ("Found the target url: " + event["params"]["response"]["url"]) + print ("Index: " + str(events.index(event))) + targetIndex = events.index(event) + break + print("check target index: " + str(targetIndex)) + if targetIndex != -1: + body = driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': events[targetIndex]["params"]["requestId"]}) + print("Here is the body: " + str(body)) + available_date = json.loads(body["body"]) + + return available_date + else: + return [] + + def login(): # Bypass reCAPTCHA driver.get(f"https://ais.usvisa-info.com/{COUNTRY_CODE}/niv") @@ -161,38 +214,142 @@ def get_time(date): return time +def select_time_manually(date): + print("---------------TEST START-----------------") + print("test date: " + str(date)) + year = date["date"].split("-")[0] + month = date["date"].split("-")[1] + day = date["date"].split("-")[2] + + datepicker = driver.find_element(By.ID, 'appointments_consulate_appointment_date') + print("datepicker: " + str(datepicker)) + datepicker.click() + time.sleep(0.5) + print("after snap") + #find the available date button + print("is there any btn?" + str(len( driver.find_elements(By.XPATH, '//a[@class="ui-state-default"]')) > 0)) + hasDate = len(driver.find_elements(By.XPATH, '//a[@class="ui-state-default"]')) > 0 + print("First hasDate: " + str(hasDate)) + while not hasDate: + print("No date available, click next month button") + print("current hasDate: " + str(hasDate)) + #click next month button + nextBtn = driver.find_element(By.XPATH, '//a[contains(@class, "ui-datepicker-next ui-corner-all")]') + print("nextBtn: " + str(nextBtn)) + nextBtn.click() + time.sleep(0.5) + #find the available date button + hasDate = len(driver.find_elements(By.XPATH, '//a[@class="ui-state-default"]')) > 0 + if hasDate == True: + hasDate = driver.find_elements(By.XPATH, '//a[@class="ui-state-default"]') + print("hasDate: " + str(hasDate)) + break + else: + continue + + parentDateBox = driver.execute_script("return arguments[0].parentNode;", hasDate[0]) + + print("web day: " + hasDate[0].text) + print("day: " + str(int(day))) + print("web month: " + str(int(parentDateBox.get_attribute("data-month")) + 1)) + print("month: " + str(int(month))) + print("web year: " + str(parentDateBox.get_attribute("data-year"))) + print("year: " + year) + + if str(int(day)) == hasDate[0].text and str(int(month)) == str(int(parentDateBox.get_attribute("data-month")) + 1) and year == str(parentDateBox.get_attribute("data-year")): + print("Found the date!") + parentDateBox.click() + time.sleep(1) # wait for all the data to arrive. + browser_log = driver.get_log('performance') + events = [process_browser_log_entry(entry) for entry in browser_log] + events = [event for event in events if 'Network.response' in event['method']] + targetIndex = -1; + for event in events: + if "response" in event["params"] and "url" in event["params"]["response"]: + if "/appointment/times/" in event["params"]["response"]["url"]: + print ("Found the target url: " + event["params"]["response"]["url"]) + print ("Index: " + str(events.index(event))) + targetIndex = events.index(event) + break + if targetIndex != -1: + body = driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': events[targetIndex]["params"]["requestId"]}) + print("Here is the TIMES body: " + str(body)) + available_times = json.loads(body["body"]) + return available_times + else: + print("Fail to get the available times request body") + return False + + + print("Final hasDate date: " + str(hasDate[0].text)) + #month count starts from 0 + print("Final hasDate month: " + str(parentDateBox.get_attribute("data-month"))) + print("Final hasDate year: " + str(parentDateBox.get_attribute("data-year"))) + #click the available date button + # for dateButton in hasDate: + # if dateButton.text == date: + # dateButton.click() + # break + + def reschedule(date): global EXIT print(f"Starting Reschedule ({date})") - time = get_time(date) - driver.get(APPOINTMENT_URL) - - data = { - "utf8": driver.find_element(by=By.NAME, value='utf8').get_attribute('value'), - "authenticity_token": driver.find_element(by=By.NAME, value='authenticity_token').get_attribute('value'), - "confirmed_limit_message": driver.find_element(by=By.NAME, value='confirmed_limit_message').get_attribute('value'), - "use_consulate_appointment_capacity": driver.find_element(by=By.NAME, value='use_consulate_appointment_capacity').get_attribute('value'), - "appointments[consulate_appointment][facility_id]": FACILITY_ID, - "appointments[consulate_appointment][date]": date, - "appointments[consulate_appointment][time]": time, - } - - headers = { - "User-Agent": driver.execute_script("return navigator.userAgent;"), - "Referer": APPOINTMENT_URL, - "Cookie": "_yatri_session=" + driver.get_cookie("_yatri_session")["value"] - } - - r = requests.post(APPOINTMENT_URL, headers=headers, data=data) - if(r.text.find('Successfully Scheduled') != -1): - msg = f"Rescheduled Successfully! {date} {time}" - send_notification(msg) + available_date = select_time_manually(date) + timesBody = select_time_manually(available_date[0]) + print("Here is the timesBody: " + str(timesBody)) + print(timesBody["available_times"]) + if timesBody != False and (len(timesBody["available_times"]) > 0): + print("if right") + timeDropDown = driver.find_element(By.ID, 'appointments_consulate_appointment_time') + timeDropDownSelect = Select(timeDropDown) + print(str(timesBody["available_times"][0])) + timeDropDownSelect.select_by_visible_text(str(timesBody["available_times"][0])) + time.sleep(0.5) + driver.find_element(By.ID, 'appointments_submit').click() + time.sleep(0.5) + driver.find_element(By.XPATH, '//a[contains(text(), "Confirm")]').click() + print(f"Scheduleing Successful in: ({date})") EXIT = True else: - msg = f"Reschedule Failed. {date} {time}" - send_notification(msg) + print("else GG") + return False + + + + +#--------------------old code-------------------- + # data = { + # "utf8": driver.find_element(by=By.NAME, value='utf8').get_attribute('value'), + # "authenticity_token": driver.find_element(by=By.NAME, value='authenticity_token').get_attribute('value'), + # "confirmed_limit_message": driver.find_element(by=By.NAME, value='confirmed_limit_message').get_attribute('value'), + # "use_consulate_appointment_capacity": driver.find_element(by=By.NAME, value='use_consulate_appointment_capacity').get_attribute('value'), + # "appointments[consulate_appointment][facility_id]": FACILITY_ID, + # "appointments[consulate_appointment][date]": date, + # "appointments[consulate_appointment][time]": time, + # } + + # headers = { + # "User-Agent": driver.execute_script("return navigator.userAgent;"), + # "Referer": APPOINTMENT_URL, + # "Cookie": "_yatri_session=" + driver.get_cookie("_yatri_session")["value"] + # } + + # print("Rescheduling...") + # print(data) + # print(headers) + + # r = requests.post(APPOINTMENT_URL, headers=headers, data=data) + # if(r.text.find('Successfully Scheduled') != -1): + # msg = f"Rescheduled Successfully! {date} {time}" + # send_notification(msg) + # EXIT = True + # else: + # msg = f"Reschedule Failed. {date} {time}" + # send_notification(msg) +#--------------------old code-------------------- def is_logged_in(): content = driver.page_source @@ -250,17 +407,20 @@ def push_notification(dates): print(f"Retry count: {retry_count}") print() - dates = get_date()[:5] - if not dates: - msg = "List is empty" - send_notification(msg) - EXIT = True + dates = get_date_new(APPOINTMENT_URL)[:5] + # if not dates: + # msg = "List is empty" + # send_notification(msg) + # EXIT = True print_dates(dates) date = get_available_date(dates) print() print(f"New date: {date}") if date: - reschedule(date) + rescheduleRes = reschedule(date) + if not rescheduleRes: + time.sleep(RETRY_TIME) + continue push_notification(dates) if(EXIT): @@ -271,7 +431,7 @@ def push_notification(dates): msg = "List is empty" send_notification(msg) #EXIT = True - time.sleep(COOLDOWN_TIME) + time.sleep(RETRY_TIME) else: time.sleep(RETRY_TIME)