├── react-app ├── src │ ├── styles.css │ ├── pages │ │ ├── HowToUse.css │ │ └── HowToUse.js │ ├── styles.js │ ├── defaultSettings.js │ ├── index.js │ ├── utils.js │ ├── contexts │ │ ├── ModalContext.js │ │ └── AppContext.js │ ├── components │ │ ├── MyDatePicker.js │ │ ├── DebugSettings.js │ │ ├── OtherPreferences.js │ │ ├── ContactDetails.js │ │ ├── CustomSwitch.js │ │ ├── MyTimePicker.js │ │ ├── TimerDetails.js │ │ ├── LoginDetails.js │ │ ├── BookingCountDown.js │ │ ├── Footer.js │ │ ├── PassengerDetails.js │ │ ├── Header.js │ │ ├── ModalPopup.js │ │ ├── TrainDetails.js │ │ ├── PaymentDetails.js │ │ ├── LogoIcon.js │ │ ├── PassengerNames.js │ │ └── PassengerList.js │ ├── App.js │ ├── apis.js │ └── theme.js ├── .babelrc ├── public │ ├── index.html │ └── icon.svg ├── .eslintrc.js ├── webpack.config.js └── package.json ├── chrome-extension ├── assets │ ├── icon.png │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-32.png │ ├── icon-48.png │ ├── icon-64.png │ └── icon.svg ├── package.json ├── manifest.json ├── webpack.content.js ├── options.css ├── src │ ├── scripts │ │ ├── utils.js │ │ ├── reviewCaptcha.js │ │ ├── logger.js │ │ ├── elementUtils.js │ │ ├── main-script.js │ │ ├── login.js │ │ ├── payment.js │ │ ├── storage.js │ │ ├── domSelectors.js │ │ ├── trainSearch.js │ │ ├── bookTicket.js │ │ └── passengerManagement.js │ └── sw.js └── how-to-use.html ├── package.json ├── .github ├── labeler.yml ├── release.yml └── workflows │ ├── pr-auto-label-and-check.yml │ └── build-and-release.yml ├── .gitignore └── README.md /react-app/src/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /chrome-extension/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon.png -------------------------------------------------------------------------------- /chrome-extension/assets/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon-128.png -------------------------------------------------------------------------------- /chrome-extension/assets/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon-16.png -------------------------------------------------------------------------------- /chrome-extension/assets/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon-32.png -------------------------------------------------------------------------------- /chrome-extension/assets/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon-48.png -------------------------------------------------------------------------------- /chrome-extension/assets/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpak-maurya/irctc-tatkal-ticket-booking/HEAD/chrome-extension/assets/icon-64.png -------------------------------------------------------------------------------- /react-app/src/pages/HowToUse.css: -------------------------------------------------------------------------------- 1 | .step-section { 2 | margin-bottom: 40px; 3 | } 4 | 5 | .info { 6 | background-color: #e7f3fe; 7 | border-left: 6px solid #2196F3; 8 | padding-left: 20px; 9 | } 10 | 11 | .warning { 12 | background-color: #ffffcc; 13 | border-left: 6px solid #f7ec8c; 14 | padding-left: 20px; 15 | } 16 | -------------------------------------------------------------------------------- /react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tatkal Ticket Booking 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tatkal-ticket-booking", 3 | "version": "1.0.0", 4 | "private": true, 5 | "workspaces": ["chrome-extension", "react-app"], 6 | "scripts": { 7 | "build:chrome": "cd chrome-extension && npm run build", 8 | "build:react": "cd react-app && npm run build", 9 | "build": "npm run build:chrome && npm run build:react", 10 | "clean": "rimraf dist" 11 | }, 12 | "devDependencies": { 13 | "rimraf": "^5.0.0" 14 | } 15 | } -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | chrome-extension: 2 | - "chrome-extension/**" 3 | 4 | react-app: 5 | - "react-app/**" 6 | 7 | feature: 8 | - "**/features/**" 9 | - "**/new/**" 10 | 11 | bug: 12 | - "**/bugfix/**" 13 | - "**/*.fix.js" 14 | 15 | ui: 16 | - "**/components/**" 17 | - "**/*.css" 18 | - "**/*.scss" 19 | 20 | refactor: 21 | - "**/refactor/**" 22 | - "**/*.ref.js" 23 | 24 | docs: 25 | - "**/*.md" 26 | - "docs/**" 27 | 28 | config: 29 | - "package.json" 30 | - "package-lock.json" 31 | - "yarn.lock" 32 | - "webpack.*.js" 33 | - ".github/workflows/**" 34 | -------------------------------------------------------------------------------- /react-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | webextensions: true, 6 | }, 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:react/recommended', 10 | 'plugin:react-hooks/recommended', 11 | ], 12 | parserOptions: { 13 | ecmaFeatures: { 14 | jsx: true, 15 | }, 16 | ecmaVersion: 12, 17 | sourceType: 'module', 18 | }, 19 | plugins: ['react'], 20 | rules: { 21 | // You can add specific rules here 22 | }, 23 | settings: { 24 | react: { 25 | version: 'detect', 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: 🚀 New Features 4 | labels: 5 | - feature 6 | - enhancement 7 | 8 | - title: 🐛 Bug Fixes 9 | labels: 10 | - bug 11 | - fix 12 | 13 | - title: 💄 UI Changes 14 | labels: 15 | - ui 16 | - design 17 | 18 | - title: 🧹 Refactoring 19 | labels: 20 | - refactor 21 | 22 | - title: 📦 Dependency Updates 23 | labels: 24 | - dependencies 25 | 26 | - title: 📝 Documentation 27 | labels: 28 | - docs 29 | 30 | - title: 🛠 Other Changes 31 | labels: 32 | - "*" 33 | -------------------------------------------------------------------------------- /react-app/src/styles.js: -------------------------------------------------------------------------------- 1 | // styles.js 2 | export const sharedStyles = { 3 | container: { 4 | mb: 3, 5 | p: 3, 6 | borderRadius: '8px', 7 | backgroundColor: 'rgb(214, 227, 255)', // Background color 8 | '&:hover': { 9 | boxShadow: 2, // Change box shadow to 2 on hover 10 | }, 11 | }, 12 | 13 | input: { 14 | backgroundColor: 'white', // Input field background color 15 | '& .MuiOutlinedInput-root': { 16 | '& fieldset': { 17 | borderColor: '#ccc', 18 | }, 19 | '&:hover fieldset': { 20 | borderColor: '#999', 21 | }, 22 | '&.Mui-focused fieldset': { 23 | borderColor: '#007BFF', 24 | }, 25 | }, 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /react-app/src/defaultSettings.js: -------------------------------------------------------------------------------- 1 | const defaultSettings = { 2 | automationStatus: false, 3 | username: '', 4 | password: '', 5 | targetTime: '09:59:53', 6 | refreshTime: 5000, 7 | trainNumber: '', 8 | from: '', 9 | to: '', 10 | quotaType: 'TATKAL', 11 | accommodationClass: '3A', 12 | dateString: '', 13 | paymentType: 'BHIM/UPI', 14 | paymentMethod: 'BHIM/ UPI/ USSD', 15 | paymentProvider: 'PAYTM', 16 | autoPay: false, 17 | mobileNumber: '', 18 | autoUpgradation: false, 19 | confirmberths: false, 20 | travelInsuranceOpted: 'yes', 21 | loginMinutesBefore: 2, 22 | passengerNames: [], 23 | masterData: false, 24 | passengerList: [], 25 | }; 26 | 27 | export default defaultSettings; 28 | -------------------------------------------------------------------------------- /react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { ThemeProvider } from '@mui/material/styles'; 4 | import { CssBaseline } from '@mui/material'; 5 | import App from './App'; 6 | import './styles.css'; 7 | import { AppProvider } from './contexts/AppContext'; 8 | import { ModalProvider } from './contexts/ModalContext'; 9 | import theme from './theme'; 10 | 11 | 12 | const root = ReactDOM.createRoot(document.getElementById('root')); 13 | root.render( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /chrome-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tatkal-ticket-booking/chrome-extension", 3 | "version": "6.0.4", 4 | "description": "Tatkal Ticket Booking Chrome Extension", 5 | "author": "Deepak Maurya", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "webpack --config webpack.content.js --mode production", 9 | "watch": "webpack --config webpack.content.js --mode development --watch", 10 | "clean": "rm -rf ../dist" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.25.9", 14 | "@babel/preset-env": "^7.25.9", 15 | "@babel/preset-react": "^7.25.9", 16 | "babel-loader": "^9.2.1", 17 | "copy-webpack-plugin": "^12.0.2", 18 | "css-loader": "^6.7.1", 19 | "fs-extra": "^11.1.0", 20 | "style-loader": "^3.3.1", 21 | "webpack": "^5.76.0", 22 | "webpack-cli": "^5.0.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /react-app/src/utils.js: -------------------------------------------------------------------------------- 1 | export const getNextDay = () => { 2 | return new Date(new Date().setDate(new Date().getDate() + 1)) 3 | .toISOString() 4 | .split('T')[0]; 5 | }; 6 | 7 | 8 | // Utility function for comparing objects excluding specified keys 9 | export const isEqual = (obj1, obj2, excludedKeys = []) => { 10 | const filteredObj1 = { ...obj1 }; 11 | const filteredObj2 = { ...obj2 }; 12 | excludedKeys.forEach((key) => { 13 | delete filteredObj1[key]; 14 | delete filteredObj2[key]; 15 | }); 16 | return JSON.stringify(filteredObj1) === JSON.stringify(filteredObj2); 17 | }; 18 | 19 | export function getTimeFromString(timeString) { 20 | const [hours, minutes, seconds] = timeString.split(':').map(Number); 21 | const currentTime = new Date(); 22 | return new Date( 23 | currentTime.getFullYear(), 24 | currentTime.getMonth(), 25 | currentTime.getDate(), 26 | hours, 27 | minutes, 28 | seconds 29 | ); 30 | } -------------------------------------------------------------------------------- /react-app/src/contexts/ModalContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ModalContext = createContext(); 5 | 6 | export const ModalProvider = ({ children }) => { 7 | const [isModalOpen, setIsModalOpen] = useState(false); 8 | const [modalConfig, setModalConfig] = useState({}); 9 | 10 | const openModal = (variant, title, message, onConfirm) => { 11 | setModalConfig({ variant, title, message, onConfirm }); 12 | setIsModalOpen(true); 13 | }; 14 | 15 | const closeModal = () => { 16 | setIsModalOpen(false); 17 | setModalConfig({}); 18 | }; 19 | 20 | return ( 21 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | export const useModalContext = () => useContext(ModalContext); 28 | 29 | ModalProvider.propTypes = { 30 | children: PropTypes.node.isRequired, 31 | }; 32 | 33 | export default ModalContext; 34 | -------------------------------------------------------------------------------- /chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tatkal Ticket Booking", 3 | "version": "3.0", 4 | "manifest_version": 3, 5 | "minimum_chrome_version": "120", 6 | "description": "Enhance your Tatkal booking speed with fast, automated clicks to secure confirmed tickets effortlessly.", 7 | "background": { 8 | "service_worker": "sw.js" 9 | }, 10 | "content_scripts": [ 11 | { 12 | "matches": ["https://www.irctc.co.in/*"], 13 | "js": ["content.js"] 14 | } 15 | ], 16 | "permissions": ["storage"], 17 | "host_permissions": ["https://www.irctc.co.in/*"], 18 | "action": { 19 | "default_icon": { 20 | "16": "assets/icon-16.png", 21 | "32": "assets/icon-32.png", 22 | "48": "assets/icon-48.png", 23 | "64": "assets/icon-64.png", 24 | "128": "assets/icon-128.png" 25 | } 26 | }, 27 | "options_page": "options.html", 28 | "icons": { 29 | "16": "assets/icon-16.png", 30 | "32": "assets/icon-32.png", 31 | "48": "assets/icon-48.png", 32 | "64": "assets/icon-64.png", 33 | "128": "assets/icon-128.png" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chrome-extension/webpack.content.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 4 | 5 | 6 | module.exports = { 7 | entry: { 8 | content: './src/scripts/user-script.js', 9 | // background: './src/background.js' 10 | }, 11 | output: { 12 | path: path.resolve(__dirname, '../dist'), 13 | filename: 'content.js', 14 | clean: true 15 | }, 16 | mode: 'production', 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.js$/, 21 | exclude: /node_modules/, 22 | use: { 23 | loader: 'babel-loader', 24 | options: { 25 | presets: ['@babel/preset-env'] 26 | } 27 | } 28 | } 29 | ] 30 | }, 31 | plugins: [ 32 | new CopyWebpackPlugin({ 33 | patterns: [ 34 | { from: "manifest.json", to: "." }, 35 | { from: "src/sw.js", to: "." }, 36 | { from: "assets", to: "assets" }, 37 | { from:"how-to-use.html", to:"."}, 38 | {from:"options.css", to:"."} 39 | ] 40 | }) 41 | ] 42 | }; 43 | -------------------------------------------------------------------------------- /react-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 3 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 4 | 5 | module.exports = { 6 | entry: { 7 | app: "./src/index.js", // Entry point for the React app 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, "../dist"), // Unified output directory 11 | filename: "[name].bundle.js", // Generates app.bundle.js 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.js$/, // Transpile JS/JSX files 17 | exclude: /node_modules/, 18 | use: "babel-loader", 19 | }, 20 | { 21 | test: /\.css$/, // Add support for CSS files 22 | use: ["style-loader", "css-loader"], 23 | }, 24 | ], 25 | }, 26 | plugins: [ 27 | new HtmlWebpackPlugin({ 28 | template: "./public/index.html", // Input HTML template 29 | filename: "options.html", // Output file (Chrome extension options page) 30 | chunks: ["app"], // Only include the React app in options.html 31 | }), 32 | new CopyWebpackPlugin({ 33 | patterns: [ 34 | { from: "public", to: "public" }, // Public assets 35 | ], 36 | }), 37 | ], 38 | mode: "production", // Use 'development' for debugging 39 | }; 40 | -------------------------------------------------------------------------------- /chrome-extension/options.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | :root { 17 | --md-sys-color-primary: rgb(65, 95, 145); 18 | --md-sys-color-on-primary: rgb(255 255 255); 19 | --md-sys-color-primary-container: rgb(214 227 255); 20 | --md-sys-color-on-primary-container: rgb(0 27 62); 21 | --md-sys-color-background: rgb(249 249 255); 22 | --md-sys-color-on-background: rgb(25 28 32); 23 | } 24 | 25 | html { 26 | padding: 0 10px; 27 | } 28 | 29 | /* Add custom styles here */ 30 | body { 31 | background-color: var(--md-sys-color-background); 32 | color: var(--md-sys-color-on-background); 33 | font-family: 'Roboto', sans-serif; 34 | } 35 | h3 { 36 | color: var(--md-sys-color-primary); 37 | } 38 | .step-section { 39 | margin-bottom: 20px; 40 | background-color: var(--md-sys-color-primary-container); 41 | border-radius: 20px; 42 | padding: 20px; 43 | } 44 | -------------------------------------------------------------------------------- /chrome-extension/src/scripts/utils.js: -------------------------------------------------------------------------------- 1 | // Function to convert month abbreviation to number 2 | export function monthToNumber(month) { 3 | const monthMap = { 4 | 'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 5 | 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12' 6 | }; 7 | return monthMap[month]; 8 | } 9 | // Function to introduce a small delay 10 | export function delay(ms) { 11 | return new Promise(resolve => setTimeout(resolve, ms)); 12 | } 13 | // Function to check if searchText exists in text 14 | export function textIncludes(text, searchText) { 15 | return text.trim().toLowerCase().includes(searchText.trim().toLowerCase()); 16 | } 17 | 18 | export async function simulateTyping(element, text) { 19 | if (!element) return; 20 | element.focus(); 21 | element.value = ''; 22 | for (let char of text) { 23 | element.value += char; 24 | // Simulate typing delay 25 | await delay(100); 26 | } 27 | } 28 | 29 | export function waitForElementToAppear(selector) { 30 | return new Promise((resolve) => { 31 | const observer = new MutationObserver((mutationsList, observer) => { 32 | const element = document.querySelector(selector); 33 | if (element) { 34 | observer.disconnect(); 35 | resolve(element); 36 | } 37 | }); 38 | observer.observe(document.body, { childList: true, subtree: true }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | /chrome-extension/node_modules 4 | /react-app/node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Testing 9 | /coverage 10 | 11 | # Production 12 | /build 13 | /dist 14 | 15 | # Misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | # Logs 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # Editor directories and files 28 | .idea 29 | .vscode 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional eslint cache 40 | .eslintcache 41 | 42 | # Optional REPL history 43 | .node_repl_history 44 | 45 | # Output of 'npm pack' 46 | *.tgz 47 | 48 | # Yarn Integrity file 49 | .yarn-integrity 50 | 51 | # dotenv environment variables file 52 | .env 53 | 54 | # parcel-bundler cache (https://parceljs.org/) 55 | .cache 56 | 57 | # Next.js build output 58 | .next 59 | 60 | # Nuxt.js build / generate output 61 | .nuxt 62 | dist 63 | 64 | # Gatsby files 65 | .cache/ 66 | # Comment in the public line in if your project uses Gatsby and not Next.js 67 | # https://nextjs.org/blog/next-9-1#public-directory-support 68 | # public 69 | 70 | # VuePress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | # TernJS port file 83 | .tern-port 84 | 85 | # Stores VSCode versions used for testing VSCode extensions 86 | .vscode-test 87 | 88 | # yarn v2 89 | .yarn/cache 90 | .yarn/unplugged 91 | .yarn/build-state.yml 92 | .yarn/install-state.gz 93 | .pnp.* 94 | v*.zip 95 | 96 | -------------------------------------------------------------------------------- /chrome-extension/src/scripts/reviewCaptcha.js: -------------------------------------------------------------------------------- 1 | import { REVIEW_SELECTORS } from './domSelectors'; 2 | import { delay, simulateTyping, waitForElementToAppear } from './utils'; 3 | import { scrollToElement } from './elementUtils'; 4 | 5 | async function handleCaptchaAndContinue() { 6 | await waitForElementToAppear(REVIEW_SELECTORS.REVIEW_CAPTCHA_IMAGE); 7 | // Find the captcha input element 8 | var captchaInput = document.getElementById(REVIEW_SELECTORS.REVIEW_CAPTCHA_INPUT); 9 | 10 | // Scroll the captcha input field into view smoothly 11 | if (captchaInput) { 12 | await scrollToElement(captchaInput); 13 | } 14 | delay(100); 15 | // Prompt the user to enter the captcha value 16 | var trainHeader = document.querySelector(REVIEW_SELECTORS.REVIEW_TRAIN_HEADER); 17 | var available = trainHeader.querySelector(REVIEW_SELECTORS.REVIEW_AVAILABLE); 18 | var waitingList = trainHeader.querySelector(REVIEW_SELECTORS.REVIEW_WAITING); 19 | var seatsAvailable = (available || waitingList)?.textContent; 20 | var captchaValue = prompt( 21 | 'Current Seats Status: ' + seatsAvailable + '\nPlease enter the Captcha:' 22 | ); 23 | 24 | // Fill the captcha input field with the provided value 25 | if (captchaInput && captchaValue) { 26 | await simulateTyping(captchaInput, captchaValue); 27 | await delay(50); 28 | } 29 | 30 | // Find the "Continue" button 31 | var continueButton = document.querySelector(REVIEW_SELECTORS.REVIEW_SUBMIT_BUTTON); 32 | 33 | // Click the "Continue" button 34 | if (continueButton) { 35 | await continueButton.click(); 36 | } 37 | } 38 | 39 | export { 40 | handleCaptchaAndContinue 41 | }; -------------------------------------------------------------------------------- /react-app/src/components/MyDatePicker.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { LocalizationProvider, DatePicker } from '@mui/x-date-pickers'; 3 | import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; 4 | import dayjs from 'dayjs'; 5 | import { useAppContext } from '../contexts/AppContext'; 6 | 7 | export default function MyDatePicker() { 8 | const { formData, handleChange } = useAppContext(); 9 | const [selectedDate, setSelectedDate] = useState(null); 10 | 11 | // Use effect to initialize the selected date from formData 12 | useEffect(() => { 13 | if (formData.dateString) { 14 | setSelectedDate(dayjs(formData.dateString, 'YYYY-MM-DD')); 15 | } 16 | }, [formData.dateString]); 17 | 18 | const handleDateChange = (newValue) => { 19 | if (newValue) { 20 | const formattedDate = newValue.format('YYYY-MM-DD'); 21 | setSelectedDate(newValue); 22 | handleChange({ target: { name: 'dateString', type: 'date', value: formattedDate } }); 23 | } else { 24 | // If the date is cleared, set selectedDate to null and update formData accordingly 25 | setSelectedDate(null); 26 | handleChange({ target: { name: 'dateString', type: 'date', value: '' } }); 27 | } 28 | }; 29 | 30 | return ( 31 | 32 | 41 | 42 | ); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /react-app/src/components/DebugSettings.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | FormControlLabel, 4 | Switch, 5 | Tooltip, 6 | } from '@mui/material'; 7 | 8 | function DebugSettings() { 9 | const [debugMode, setDebugMode] = React.useState(false); 10 | 11 | React.useEffect(() => { 12 | if (typeof chrome !== 'undefined' && chrome.storage) { 13 | chrome.storage.local.get('debugMode', function(result) { 14 | setDebugMode(result.debugMode || false); 15 | }); 16 | } else { 17 | console.warn('Chrome storage is not available.'); 18 | } 19 | }, []); 20 | 21 | const handleDebugModeChange = (event) => { 22 | const newDebugMode = event.target.checked; 23 | setDebugMode(newDebugMode); 24 | if (typeof chrome !== 'undefined' && chrome.storage) { 25 | chrome.storage.local.set({ debugMode: newDebugMode }, function() { 26 | if (chrome.runtime.lastError) { 27 | console.error('Error saving debug mode:', chrome.runtime.lastError); 28 | } 29 | }); 30 | } 31 | }; 32 | 33 | return ( 34 | 35 | 44 | } 45 | label="Debug Mode" 46 | sx={{ 47 | ml: 0, 48 | '& .MuiFormControlLabel-label': { 49 | fontSize: '0.875rem', 50 | color: 'text.secondary' 51 | } 52 | }} 53 | /> 54 | 55 | ); 56 | } 57 | 58 | export default DebugSettings; -------------------------------------------------------------------------------- /chrome-extension/src/scripts/logger.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | const Logger = { 4 | isEnabled: false, 5 | 6 | enable: function() { 7 | this.isEnabled = true; 8 | }, 9 | 10 | disable: function() { 11 | this.isEnabled = false; 12 | }, 13 | 14 | _getCallerInfo: function() { 15 | const error = new Error(); 16 | const stack = error.stack.split('\n')[3]; // Get caller's stack info 17 | const match = stack.match(/at\s+(?:\w+\s+)?\(?(.+):(\d+):(\d+)\)?/); 18 | if (match) { 19 | const [, file, line] = match; 20 | const fileName = file.split('/').pop(); // Get just the filename 21 | return `[${fileName}:${line}]`; 22 | } 23 | return ''; 24 | }, 25 | 26 | log: function(...args) { 27 | if (this.isEnabled) { 28 | const callerInfo = this._getCallerInfo(); 29 | console.log(`[IRCTC-Bot]${callerInfo}:`, ...args); 30 | } 31 | }, 32 | 33 | error: function(...args) { 34 | if (this.isEnabled) { 35 | const callerInfo = this._getCallerInfo(); 36 | console.error(`[IRCTC-Bot]${callerInfo}:`, ...args); 37 | } 38 | }, 39 | 40 | warn: function(...args) { 41 | if (this.isEnabled) { 42 | const callerInfo = this._getCallerInfo(); 43 | console.warn(`[IRCTC-Bot]${callerInfo}:`, ...args); 44 | } 45 | }, 46 | 47 | info: function(...args) { 48 | if (this.isEnabled) { 49 | const callerInfo = this._getCallerInfo(); 50 | console.info(`[IRCTC-Bot]${callerInfo}:`, ...args); 51 | } 52 | } 53 | }; 54 | 55 | // Enable/disable logging based on storage setting 56 | chrome.storage.local.get('debugMode', function(result) { 57 | if (result.debugMode) { 58 | Logger.enable(); 59 | } else { 60 | Logger.disable(); 61 | } 62 | }); 63 | 64 | export default Logger; 65 | 66 | -------------------------------------------------------------------------------- /react-app/src/App.js: -------------------------------------------------------------------------------- 1 | // App.js 2 | import React from 'react'; 3 | import { Container, Stack, Box, useMediaQuery } from '@mui/material'; 4 | import LoginDetails from './components/LoginDetails'; 5 | import TrainDetails from './components/TrainDetails'; 6 | import PaymentDetails from './components/PaymentDetails'; 7 | import TimerDetails from './components/TimerDetails'; 8 | import PassengerDetails from './components/PassengerDetails'; 9 | import ContactDetails from './components/ContactDetails'; 10 | import OtherPreferences from './components/OtherPreferences'; 11 | import Header from './components/Header'; 12 | import Footer from './components/Footer'; // Import the new Footer component 13 | 14 | 15 | const App = () => { 16 | const isSmallScreen = useMediaQuery((theme) => theme.breakpoints.down('sm')); 17 | return ( 18 | 19 |
20 | 21 | {/* Updated layout for Login+Timer, Train, and Payment Details */} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |