Skip to content

Commit

Permalink
Faster Captcha Retrieval Using Local Flask Server (#88)
Browse files Browse the repository at this point in the history
* add passenger data

* add flash server to handle captha for faster captha resolution

* add .gitignore

* rollback passenger details

* update code to club everything together[one command and booking done]

* add wait on as dev dependencies
  • Loading branch information
hanisntsolo authored Nov 21, 2024
1 parent 88be91a commit 3361382
Show file tree
Hide file tree
Showing 12 changed files with 465 additions and 192 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ node_modules/
cypress/videos/
cypress/screenshots/
irctc-captcha-solver/__pycache__
cypress/downloads/
cypress/downloads/"cypress.env.json"
cypress.env.json
249 changes: 112 additions & 137 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
const MANUAL_CAPTCHA = Cypress.env("MANUAL_CAPTCHA");

Cypress.on("uncaught:exception", (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
// returning false here prevents Cypress from failing the test
return false;
});

Expand All @@ -35,168 +34,144 @@ Cypress.Commands.add(
);

function performLogin(LOGGED_IN) {
// if starts
if (!LOGGED_IN) {
cy.wait(500);

cy.get("body")
.should("be.visible")
.then((el) => {
if (el[0].innerText.includes("Logout")) {
cy.task(
"log",
"We have logged in successfully at this stage"
);
} else if (
el[0].innerText.includes("FORGOT ACCOUNT DETAILS") &&
!el[0].innerText.includes("Please Wait...")
) {
if (MANUAL_CAPTCHA) {
cy.get("#captcha").focus();
// wait for user to manually enter captcha and login
cy.get(".search_btn.loginText")
.should("include.text", "Logout")
.then(() => {
performLogin(true);
});
} else {
// get captcha value base64 starts---------
cy.get(".captcha-img")
.invoke("attr", "src")
.then((value) => {
// api call to retrieve captcha value

cy.exec(
`py irctc-captcha-solver/app.py "${value}"`
).then((result) => {
cy.get("#captcha")
.clear()
.type(result.stdout)
.type("{enter}");
// cy.contains('SIGN IN').click()
.should("be.visible")
.then((el) => {
if (el[0].innerText.includes("Logout")) {
cy.task("log", "We have logged in successfully at this stage");
} else if (
el[0].innerText.includes("FORGOT ACCOUNT DETAILS") &&
!el[0].innerText.includes("Please Wait...")
) {
if (MANUAL_CAPTCHA) {
cy.get("#captcha").focus();
// Wait for user to manually enter captcha and login
cy.get(".search_btn.loginText")
.should("include.text", "Logout")
.then(() => {
performLogin(true);
});
} else {
// Use the local server to solve the captcha
cy.get(".captcha-img")
.invoke("attr", "src")
.then((value) => {
// API call to retrieve captcha value
cy.request({
method: "POST",
url: "http://localhost:5000/extract-text", // URL of the Flask server endpoint
body: {
image: value, // Assuming `value` is your base64 image string
},
}).then((response) => {
const extractedText = response.body.extracted_text; // Access the extracted text from the server response
cy.get("#captcha")
.clear()
.type(extractedText)
.type("{enter}");

cy.get("body").then((el) => {
if (
el[0].innerText.includes(
"Invalid Captcha"
)
) {
performLogin(false);
} else if (
el[0].innerText.includes("Logout")
) {
performLogin(true);
} else {
performLogin(false);
}
});
});
// api call to retrieve captcha value
cy.get("body").then((el) => {
if (el[0].innerText.includes("Invalid Captcha")) {
performLogin(false);
} else if (el[0].innerText.includes("Logout")) {
performLogin(true);
} else {
performLogin(false);
}
});
// get captcha value base64 ends---------
}
} else {
performLogin(false);
});
});
}
});
} else {
performLogin(false);
}
});
}
// if ends
}

let MAX_ATTEMPT = 120;
// function to solveCaptcha after logging in

function solveCaptcha() {
// Max attempt for this function to fail
MAX_ATTEMPT -= 1;
cy.wrap(MAX_ATTEMPT, { timeout: 10000 }).should("be.gt", 0);

// cy.task("log", `Calling solveCaptcha() ${MAX_ATTEMPT}th time`);

cy.wait(500);
cy.get("body")
.should("be.visible")
.then((el) => {
if (
el[0].innerText.includes(
"Unable to process current transaction"
) &&
el[0].innerText.includes("Payment Mode")
) {
// cy.task("log", "Unable to process current transaction...");
cy.get(".train_Search").click();
cy.wait(1000);
}
.should("be.visible")
.then((el) => {
if (
el[0].innerText.includes(
"Unable to process current transaction"
) &&
el[0].innerText.includes("Payment Mode")
) {
cy.get(".train_Search").click();
cy.wait(1000);
}

if (el[0].innerText.includes("Sorry!!! Please Try again!!")) {
throw new Error(
"Sorry!!! Please Try again!! <<< Thrown By IRCTC"
);
}
if (el[0].innerText.includes("Sorry!!! Please Try again!!")) {
throw new Error("Sorry!!! Please Try again!! <<< Thrown By IRCTC");
}

if (el[0].innerText.includes("Payment Methods")) {
// cy.task("log", "CAPTCHA .... SOLVED");
return;
}
if (el[0].innerText.includes("Payment Methods")) {
return;
}

if (el[0].innerText.includes("No seats available")) {
cy.fail(
"Further execution stopped because there are no more tickets."
);
}
if (el[0].innerText.includes("No seats available")) {
cy.fail("Further execution stopped because there are no more tickets.");
}

// Check whether we are at reviewBooking stage or not if yes keep on solving captcha
if (
el[0].innerText.includes("Your ticket will be sent to") &&
!el[0].innerText.includes("Please Wait...") &&
el[0].innerHTML.includes("Enter Captcha")
) {
if (MANUAL_CAPTCHA) {
cy.get("#captcha").focus();
cy.get("body").then((el) => {
if (el[0].innerText.includes("Payment Methods")) {
cy.task("log", "Bypassed Captcha");
}
});
} else {
cy.get(".captcha-img")
.invoke("attr", "src")
.then((value) => {
// Use the local server to solve the captcha
cy.request({
method: "POST",
url: "http://localhost:5000/extract-text", // URL of the Flask server endpoint
body: {
image: value, // Assuming `value` is your base64 image string
},
}).then((response) => {
const extractedText = response.body.extracted_text;
cy.get("#captcha")
.clear()
.type(extractedText)
.type("{enter}");

if (
el[0].innerText.includes("Your ticket will be sent to") &&
!el[0].innerText.includes("Please Wait...") &&
el[0].innerHTML.includes("Enter Captcha")
) {
if (MANUAL_CAPTCHA) {
cy.get("#captcha").focus();
cy.get("body").then((el) => {
if (el[0].innerText.includes("Payment Methods")) {
// cy.task("log", "Bypassed Captcha");
}
});
} else {
// get captcha value base64 starts---------
cy.get(".captcha-img")
.invoke("attr", "src")
.then((value) => {
// api call to retrieve captcha value
cy.exec(
`py irctc-captcha-solver/app.py "${value}"`
).then((result) => {
cy.get("#captcha")
.clear()
.type(result.stdout)
.type("{enter}");
cy.get("body").then((el) => {
if (
el[0].innerText.includes(
"Payment Methods"
)
) {
cy.task("log", "Bypassed Captcha");
} else {
solveCaptcha();
}
});
});
// api call to retrieve captcha value
cy.get("body").then((el) => {
if (el[0].innerText.includes("Payment Methods")) {
cy.task("log", "Bypassed Captcha");
} else {
solveCaptcha();
}
});
// get captcha value base64 ends---------

// recursing until captcha gets solved
solveCaptcha();
}
} else if (el[0].innerText.includes("Payment Methods")) {
return;
} else {
});
});
solveCaptcha();
}
});
} else if (el[0].innerText.includes("Payment Methods")) {
return;
} else {
solveCaptcha();
}
});
}

function BOOK_UNTIL_TATKAL_OPENS(
Expand Down
Binary file not shown.
Binary file added irctc-captcha-solve-server/captcha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added irctc-captcha-solver/EasyOCR/craft_mlt_25k.pth
Binary file not shown.
Binary file added irctc-captcha-solver/EasyOCR/english_g2.pth
Binary file not shown.
72 changes: 72 additions & 0 deletions irctc-captcha-solver/app-server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import argparse
import numpy as np
from PIL import Image
import io
import base64
import easyocr
from flask import Flask, request, jsonify

# Initialize EasyOCR Reader
reader = easyocr.Reader(["en"], model_storage_directory="./EasyOCR")

# Initialize Flask app
app = Flask(__name__)

def extract_text_from_image(base64_image):
try:
# Convert the base64 image to bytes
image_bytes = base64.b64decode(base64_image[22:])
# Create a BytesIO object from the image bytes
image_buffer = io.BytesIO(image_bytes)
# Open the image using PIL (Python Imaging Library)
image = Image.open(image_buffer)
# Convert the image to grayscale (optional but can improve OCR accuracy)
image = image.convert("L")

# Convert PIL image to OpenCV format
open_cv_image = np.array(image)

result = reader.readtext(open_cv_image, detail=0)
if result:
return result[0].replace(" ", "")
else:
return "ABCDEF"
except Exception as e:
return f"Error processing image: {str(e)}"

@app.route("/extract-text", methods=["POST"])
def extract_text():
# Get the base64 image from the request
data = request.get_json()
base64_image = data.get("image", "")

if not base64_image:
return jsonify({"error": "No base64 image string provided"}), 400

extracted_text = extract_text_from_image(base64_image)
return jsonify({"extracted_text": extracted_text})

@app.route('/')
def health_check():
return "Server is running", 200

if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Run the OCR extraction server."
)
parser.add_argument(
"--host",
type=str,
default="0.0.0.0",
help="Host address to run the server on (default: 0.0.0.0)",
)
parser.add_argument(
"--port",
type=int,
default=5000,
help="Port to run the server on (default: 5000)",
)
args = parser.parse_args()

# Run Flask server
app.run(host=args.host, port=args.port)
Loading

0 comments on commit 3361382

Please sign in to comment.