Skip to content

Commit

Permalink
change captcha mode
Browse files Browse the repository at this point in the history
  • Loading branch information
phamleduy04 committed Aug 6, 2024
1 parent 116765a commit 34dac0b
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 33 deletions.
4 changes: 2 additions & 2 deletions example.config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ location:
end: 18

appSettings:
# https://github.com/phamleduy04/texas-dps-scheduler/wiki/How-to-get-Auth-Token
authToken: ''
# https://github.com/phamleduy04/texas-dps-scheduler/wiki/How-to-get-Captcha-Token
captchaToken: ''
# Put true/false without quotes if you want to cancel the appointment automatically if found existing appointment
cancelIfExist: false
# The time interval (in ms) the app will recheck for newer dates
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "texas-dps-scheduler",
"version": "4.0.0-beta3",
"version": "4.1.0-beta1",
"description": "Texas DPS Automatic Scheduler",
"main": "dist/index.js",
"scripts": {
Expand Down
33 changes: 16 additions & 17 deletions src/Browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import AnonymizeUA from 'puppeteer-extra-plugin-anonymize-ua';
// This plugin use adblocker (bc imleague was bloated with ads!!!!)
import AdblockerPlugin from 'puppeteer-extra-plugin-adblocker';

import type { AuthPayload } from '../Interfaces/Auth';

puppeteer.use(AdblockerPlugin({ blockTrackers: true }));
puppeteer.use(StealthPlugin());
puppeteer.use(AnonymizeUA());

export const getAuthToken = async (): Promise<string> => {
export const getCaptchaToken = async (): Promise<string> => {
try {
// Launch brower instance
const browser = await puppeteer.launch({
Expand Down Expand Up @@ -69,21 +71,17 @@ export const getAuthToken = async (): Promise<string> => {
await page.setRequestInterception(true);
log.dev('Request interception enabled');

const authTokenPromise = new Promise(async (resolve, reject) => {
const captchaTokenPromise = new Promise(async (resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Auth token retrieval timed out after 60 seconds')), 60000);

// Listen for network requests
page.on('request', async request => request.continue());

// Listen for network responses
page.on('response', async response => {
const url = response.url();
log.dev(`URL: ${url}`);
if (url === 'https://apptapi.txdpsscheduler.com/api/auth' && response.request().method() == 'POST') {
const token = await response.text();
page.on('request', async request => {
if (request.url() === 'https://apptapi.txdpsscheduler.com/api/auth' && request.method() == 'POST') {
const postData = JSON.parse(request.postData()) as AuthPayload;
clearTimeout(timeout);
resolve(token);
resolve(postData.RecaptchaToken.Token);
}
request.continue();
});

// Click the login button
Expand All @@ -92,18 +90,19 @@ export const getAuthToken = async (): Promise<string> => {
});

// Wait for the auth token
const authToken = (await authTokenPromise) as string;
const captchaToken = (await captchaTokenPromise) as string;

// Close the browser
await browser.close();

log.dev(`Auth token: ${authToken}`);
return authToken;
log.info('Get captcha token successfully!');
log.dev(`Captcha token: ${captchaToken}`);
return captchaToken;
} catch (err) {
log.error('Error while getting auth token: ', err as Error);
log.info('Try to get auth token again or manual set it in config.yml');
log.error('Error while getting captcha token: ', err as Error);
log.info('Try to get captcha token again or manual set it in config.yml');
process.exit(1);
}
};

if (process.env.NODE_ENV === 'development') getAuthToken();
if (process.env.NODE_ENV === 'development') getCaptchaToken();
54 changes: 42 additions & 12 deletions src/Client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import parseConfig from '../Config';
import * as log from '../Log';
import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';
import { getAuthToken } from '../Browser';
import { getCaptchaToken } from '../Browser';
dayjs.extend(isBetween);
import prompts from 'prompts';
import type { EligibilityPayload } from '../Interfaces/Eligibility';
Expand All @@ -15,6 +15,7 @@ import type { HoldSlotPayload, HoldSlotResponse } from '../Interfaces/HoldSlot';
import type { BookSlotPayload, BookSlotResponse } from '../Interfaces/BookSlot';
import type { ExistBookingPayload, ExistBookingResponse } from '../Interfaces/ExistBooking';
import type { CancelBookingPayload } from '../Interfaces/CancelBooking';
import type { AuthPayload } from 'src/Interfaces/Auth';

import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import randomUseragent from 'random-useragent';
Expand Down Expand Up @@ -44,6 +45,7 @@ class TexasScheduler {
private isHolded = false;
private queue = new pQueue({ concurrency: 1 });
private userAgent = randomUseragent.getRandom();
private authToken = null;

public constructor() {
// eslint-disable-next-line @typescript-eslint/no-var-requires, prettier/prettier
Expand All @@ -55,11 +57,7 @@ class TexasScheduler {
}

public async run() {
if (!this.config.appSettings.authToken || this.config.appSettings.authToken.length === 0) {
log.info('Auth Token is not set, requesting one...');
this.config.appSettings.authToken = await getAuthToken();
log.info(`Auth Token: ${this.config.appSettings.authToken}`);
}
await this.setAuthToken();
this.existBooking = await this.checkExistBooking();
const { exist, response } = this.existBooking;
if (exist) {
Expand Down Expand Up @@ -241,15 +239,17 @@ class TexasScheduler {
}

private async requestApi(path: string, method: 'GET' | 'POST', body: object, retryTime = 0): Promise<Dispatcher.ResponseData> {
const headers = {
'Content-Type': 'application/json;charset=UTF-8',
Origin: 'https://public.txdpsscheduler.com',
'User-Agent': this.userAgent,
};
if (this.authToken) headers['Authorization'] = this.authToken;

const response = await this.requestInstance.request({
method,
path,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
Origin: 'https://public.txdpsscheduler.com',
'User-Agent': this.userAgent,
Authorization: this.config.appSettings.authToken,
},
headers,
headersTimeout: this.config.appSettings.headersTimeout,
body: JSON.stringify(body),
});
Expand Down Expand Up @@ -338,6 +338,36 @@ class TexasScheduler {
log.error(await response.body.text());
}
}

private async setAuthToken() {
if (!this.config.appSettings.captchaToken) {
log.info('No captcha token found! Will try to get one....');
this.config.appSettings.captchaToken = await getCaptchaToken();
}

const requestBody: AuthPayload = {
UserName: `${this.config.personalInfo.firstName}_${this.config.personalInfo.lastName}_${this.config.personalInfo.lastFourSSN}`,
RecaptchaToken: {
Action: 'Login',
Token: this.config.appSettings.captchaToken,
},
};

const response = await this.requestApi('/api/Auth', 'POST', requestBody);

if (response.statusCode === 200) {
const token = (await response.body.text()) as string;
if (token === null) {
log.error('Failed to get auth token');
process.exit(1);
}
this.authToken = token;
} else {
log.error('Failed to get auth token');
process.exit(1);
}
log.info(`Refreshed auth token: ${this.authToken}`);
}
}

export default TexasScheduler;
7 changes: 7 additions & 0 deletions src/Interfaces/Auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface AuthPayload {
UserName: string;
RecaptchaToken: {
Action: 'Login';
Token: string;
};
}
2 changes: 1 addition & 1 deletion src/Interfaces/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const configZod = z.object({
.refine(checkStartLowerThanEnd, { message: 'Start number must be lower than end number' }),
}),
appSettings: z.object({
authToken: z.string().default(''),
captchaToken: z.string().default(''),
cancelIfExist: z.boolean().default(false),
interval: z.number().default(10000),
webserver: z.boolean().default(false),
Expand Down

0 comments on commit 34dac0b

Please sign in to comment.