Skip to content

Instantly share code, notes, and snippets.

@yaojialyu
Created November 5, 2020 09:24
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save yaojialyu/0c59c23d84585cc6e889e394d928a164 to your computer and use it in GitHub Desktop.
Save yaojialyu/0c59c23d84585cc6e889e394d928a164 to your computer and use it in GitHub Desktop.
ais usvisa reschedule
# -*- coding: utf8 -*-
import time
import json
import random
import platform
from datetime import datetime
import requests
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as Wait
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
USERNAME = '<username>'
PASSWORD = '<pwd>'
SCHEDULE = '<schedule number>'
PUSH_TOKEN = '<my push token>'
PUSH_USER = '<my push user>'
MY_SCHEDULE_DATE = "<current date>" # 2020-12-02
#MY_CONDITION = lambda month,day: int(month) == 11 or (int(month) == 12 and int(day) <=5)
MY_CONDITION = lambda month,day: int(month) == 11 and int(day) >= 5
SLEEP_TIME = 5 # recheck time interval
DATE_URL = "https://ais.usvisa-info.com/en-ec/niv/schedule/%s/appointment/days/108.json?appointments[expedite]=false" % SCHEDULE
TIME_URL = "https://ais.usvisa-info.com/en-ec/niv/schedule/%s/appointment/times/108.json?date=%%s&appointments[expedite]=false" % SCHEDULE
APPOINTMENT_URL = "https://ais.usvisa-info.com/en-ec/niv/schedule/%s/appointment" % SCHEDULE
HUB_ADDRESS = 'http://localhost:4444/wd/hub'
EXIT = False
def send(msg):
url = "https://api.pushover.net/1/messages.json"
data = {
"token": PUSH_TOKEN,
"user": PUSH_USER,
"message": msg
}
requests.post(url, data)
def get_drive():
local_use = platform.system() == 'Darwin'
if local_use:
dr = webdriver.Chrome(executable_path = './chromedriver')
else:
dr= webdriver.Remote(command_executor=HUB_ADDRESS, desired_capabilities=DesiredCapabilities.CHROME)
return dr
driver = get_drive()
def login():
# Bypass reCAPTCHA
driver.get("https://ais.usvisa-info.com/en-ec/niv")
time.sleep(1)
a = driver.find_element_by_xpath('//a[@class="down-arrow bounce"]')
a.click()
time.sleep(1)
print("start sign")
href = driver.find_element_by_xpath('//*[@id="header"]/nav/div[2]/div[1]/ul/li[3]/a')
href.click()
time.sleep(1)
Wait(driver, 60).until(EC.presence_of_element_located((By.NAME, "commit")))
print("click bounce")
a = driver.find_element_by_xpath('//a[@class="down-arrow bounce"]')
a.click()
time.sleep(1)
do_login_action()
def do_login_action():
print("input email")
user = driver.find_element_by_id('user_email')
user.send_keys(USERNAME)
time.sleep(random.randint(1, 3))
print("input pwd")
pw = driver.find_element_by_id('user_password')
pw.send_keys(PASSWORD)
time.sleep(random.randint(1, 3))
print("click privacy")
box = driver.find_element_by_class_name('icheckbox')
box .click()
time.sleep(random.randint(1, 3))
print("commit")
btn = driver.find_element_by_name('commit')
btn.click()
time.sleep(random.randint(1, 3))
Wait(driver, 60).until(EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'Continue')]")))
print("Login successfully! ")
def get_date():
driver.get(DATE_URL)
if not is_logined():
login()
return get_date()
else:
content = driver.find_element_by_tag_name('pre').text
date = json.loads(content)
return date
def get_time(date):
time_url = TIME_URL % date
driver.get(time_url)
content = driver.find_element_by_tag_name('pre').text
data = json.loads(content)
time = data.get("available_times")[-1]
print("Get time successfully!")
return time
def reschedule(date):
global EXIT
print("Start Reschedule")
time = get_time(date)
driver.get(APPOINTMENT_URL)
data = {
"utf8": driver.find_element_by_name('utf8').get_attribute('value'),
"authenticity_token": driver.find_element_by_name('authenticity_token').get_attribute('value'),
"confirmed_limit_message": driver.find_element_by_name('confirmed_limit_message').get_attribute('value'),
"use_consulate_appointment_capacity": driver.find_element_by_name('use_consulate_appointment_capacity').get_attribute('value'),
"appointments[consulate_appointment][facility_id]": "108",
"appointments[consulate_appointment][date]": date,
"appointments[consulate_appointment][time]": time,
}
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
"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):
print("Successfully Rescheduled")
send("Successfully Rescheduled")
EXIT = True
else:
print("ReScheduled Fail")
send("ReScheduled Fail")
def is_logined():
content = driver.page_source
if(content.find("error") != -1):
return False
return True
def print_date(dates):
for d in dates:
print("%s \t business_day: %s" %(d.get('date'), d.get('business_day')))
print()
last_seen = None
def get_available_date(dates):
global last_seen
def is_earlier(date):
return datetime.strptime(MY_SCHEDULE_DATE, "%Y-%m-%d") > datetime.strptime(date, "%Y-%m-%d")
for d in dates:
date = d.get('date')
if is_earlier(date) and date != last_seen:
_, month, day = date.split('-')
if(MY_CONDITION(month, day)):
last_seen = date
return date
def push_notification(dates):
msg = "date: "
for d in dates:
msg = msg + d.get('date') + '; '
send(msg)
if __name__ == "__main__":
login()
retry_count = 0
while 1:
if retry_count > 6:
break
try:
print(datetime.today())
print("------------------")
dates = get_date()[:5]
print_date(dates)
date = get_available_date(dates)
if date:
reschedule(date)
push_notification(dates)
if(EXIT):
break
time.sleep(SLEEP_TIME)
except:
retry_count += 1
time.sleep(60*5)
if(not EXIT):
send("HELP! Crashed.")
@fredthedead
Copy link

Hey,
Many thanks for sharing your code 🙏 I wrote something similar but ran into a problem with both my code and yours (which is why it's great to meet someone who might have solved it)

The problem is that after 'clicking' the sign-in button, the page refreshes without successfully logging in. This only happens in the webdriver/selenium session, in a regular browser, with manual login, everything works fine. This makes me think the webdriver is being detected and blocked somehow. I tried everything I could to bypass the block - from changing IPs to solving reCaptchas, modifying the user-agent, using undetected_chromedriver, and whatnot.

Have you run into this issue when running your script? Any recommendation on how to overcome it?

Again, many thanks for your help in advance

@yaojialyu
Copy link
Author

yaojialyu commented Nov 1, 2021

Hi @fredthedead ,
Thanks for your comment.
The reason fails to log in is that this page is using Google reCAPTCHA v3, you can see this sign on the right bottom. It may recognize some patterns if you try to log in frequently (I guess), and I met the situation you mentioned before.

The way I solved this problem is to simulate a "real" person's browsing activities. You can see inside login function, there are some actions before the actual login operation and some sleep intervals after typing in username and password. If you already encountered this problem, it'd be better to retry later.

I tested and run my code just now and it logs in successfully.

image

@fredthedead
Copy link

Many thanks for your reply and suggestions 🙏. Will try again now that some time has passed

@Nemeziz
Copy link

Nemeziz commented Dec 12, 2021

¿Still woks?
Sigue funcionando tu script?

@halizadeh91
Copy link

After login i am getting {message:Facility is not valid,error:bad_request} error!
Btw, I am using Webdriver_Manager this way: from webdriver_manager.chrome import ChromeDriverManager
and the code is running on terminal mac os.
🙏🏻

@Svision
Copy link

Svision commented Feb 22, 2022

^ I’m having the same issue here.
I’m not quite sure how this DATE_URL works
DATE_URL = "https://ais.usvisa-info.com/en-ca/niv/schedule/%s/appointment/days/108.json?appointments[expedite]=false" % SCHEDULE
What is this 108.json thingy doing at the end anyway? Should I change this?

@Svision
Copy link

Svision commented Feb 22, 2022

UPDATE:
I find out what that 108 means now. It represent the facility_id, to find out which facility you want to attend. Use F12 or inspect on appointment page, and, in my case, you can see something like
<option data-collects-biometrics="false" selected="selected" value="94">Toronto</option>
The value number is the facility_id, replace 108 in the code with your <facility_id> should work.

@roibWix
Copy link

roibWix commented Mar 7, 2022

Hey,
Did you guys got blocked after a few attempts for a couple of hours?
if not, did you change the sleep_time interval? did you do something else?

@renoyin
Copy link

renoyin commented Mar 7, 2022

The script works for me on my mac. When I try to run it inside a linux VM on Azure, however, it fails to log in after clicking the signin button. Tried using varying sleep interval but did not help. Manual login inside VM through xrdp works though. I suspect reCAPTCHA detects that the input speed of username and password is too fast. Just my two cents, not sure how reCAPTCHA works and what else of the workflow differs from real human beings.

Also, saw people on the internet saying there's a limit of the number of reschedules an application can make before re-pay the appointment fee. Typically 3 (the initial one + 2 changes) from what I could find. Currently I have made 2 reschedules so trying to be cautious before making any changes. Would appreciate if anyone knows more about the limit.

@yaojialyu
Copy link
Author

Hey guys, I wrote this script 16 months ago and I haven't tested it for a long time, things may be changed now. So that I cannot provide more insights/solutions for what you mentioned above. Please refer to others' replies. Good luck to you all.

One question, is there some place that refers to this script? since these days the replies become more than ever before.

@renoyin
Copy link

renoyin commented Mar 10, 2022

I just randomly came across this gist while searching ais on github lol.. Thanks for sharing :)

@Svision
Copy link

Svision commented Mar 16, 2022

The reschedule(date) method doesn't seem to work. Did anyone write a functional version? Appreciated it.
I modified the original script and included logging: https://gist.github.com/Svision/04202d93fb32d14f00ac746879820722

@israteneda
Copy link

israteneda commented Jun 30, 2022

@Svision I saw an implementation of @yaojialyu's gist here: https://github.com/uxDaniel/visa_rescheduler

@gustavodearmas
Copy link

Lo único malo es que el servidor lo bloquea a uno por entrar mucho :(
Yo Hice un boot también, busco la manera acceder a api importando request, logro solo iniciar sesión, de ahí no he pasado pasar. Alguna forma de guardar hacer request.Session() y que no se pierda el registro cuando le hago peticiones get a la página de appoiment?

@SZ-Edward
Copy link

Hi @yaojialyu , can you tell me what is number 108 for in the DATE_URL and TIME_URL please?

@groverjatin17
Copy link

Hi guys, I see that the APIs does not work if we call them from the codebase. Have the US Gov made changes in the server to not accept APIs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment