From 3933128ed8aa29cd6f343b16f4b83008b69b4e1b Mon Sep 17 00:00:00 2001 From: shaamilahmed Date: Sun, 12 Apr 2026 11:02:42 +0500 Subject: [PATCH] Completed Payment and Timings (not tested during 8AM timing yet.) --- .gitignore | 2 ++ BadmintonBooker.csproj | 2 ++ GoogleMessages.cs | 63 ++++++++++++++++++++++++++++++++++ OTPExtractor.cs | 29 ---------------- Program.cs | 76 ++++++++++++++++++++++++++++++++++++++++-- README.md | 8 ++++- 6 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 GoogleMessages.cs delete mode 100644 OTPExtractor.cs diff --git a/.gitignore b/.gitignore index f4baa2b..ff46230 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ [Pp]assword.txt +CVV.txt +CardDetails.txt # ---> VisualStudioCode .vscode/* diff --git a/BadmintonBooker.csproj b/BadmintonBooker.csproj index b2fd81a..86b7829 100644 --- a/BadmintonBooker.csproj +++ b/BadmintonBooker.csproj @@ -14,6 +14,8 @@ + + diff --git a/GoogleMessages.cs b/GoogleMessages.cs new file mode 100644 index 0000000..2a78cb1 --- /dev/null +++ b/GoogleMessages.cs @@ -0,0 +1,63 @@ +using System.Text.RegularExpressions; +using Microsoft.Playwright; + +namespace BadmintonBooker; + +public static class GoogleMessages +{ + public static async Task GetEgovOTP(IBrowserContext context, IPage originalPage) + { + var msgPage = await context.NewPageAsync(); + await msgPage.GotoAsync("https://messages.google.com/web/u/0/conversations/CgjwUsdGGwxxGhIBNw", new PageGotoOptions { WaitUntil = WaitUntilState.Load }); + + var messageElement = msgPage.Locator("mws-text-message-part").Last; + string messageText = await messageElement.InnerTextAsync(); + + await originalPage.BringToFrontAsync(); + + var match = Regex.Match(messageText, @"\b\d{5,}\b"); + if (match.Success) + { + return match.Value; + } + else + { + Console.WriteLine("No OTP found in the message."); + return null; + } + } + + public static async Task GetBMLOTP(IBrowserContext context, IPage originalPage) + { + var msgPage = await context.NewPageAsync(); + await msgPage.GotoAsync("https://messages.google.com/web/u/0/conversations/Cgj4KNDeDzFopxIBMQ", new PageGotoOptions { WaitUntil = WaitUntilState.Load }); + + var messageElement = msgPage.Locator("mws-text-message-part").Last; + string messageText = await messageElement.InnerTextAsync(); + + await originalPage.BringToFrontAsync(); + + var match = Regex.Match(messageText, @"\b\d{6,}\b"); + if (match.Success) + { + return match.Value; + } + else + { + Console.WriteLine("No OTP found in the message."); + return null; + } + } + + public static async Task GetMessageFromHDC(IBrowserContext context, IPage originalPage) + { + var msgPage = await context.NewPageAsync(); + await msgPage.GotoAsync("https://messages.google.com/web/u/0/conversations/Cghm7Ql96iXyyhIBMg", new PageGotoOptions { WaitUntil = WaitUntilState.Load }); + + var messageElement = msgPage.Locator("mws-text-message-part").Last; + string messageText = await messageElement.InnerTextAsync(); + + await originalPage.BringToFrontAsync(); + return messageText; + } +} \ No newline at end of file diff --git a/OTPExtractor.cs b/OTPExtractor.cs deleted file mode 100644 index 28d76da..0000000 --- a/OTPExtractor.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text.RegularExpressions; -using Microsoft.Playwright; - -namespace BadmintonBooker; - -public static class OTPExtractor -{ - public static async Task GetEgovOTP(IBrowserContext context, IPage originalPage) - { - var msgPage = await context.NewPageAsync(); - await msgPage.GotoAsync("https://messages.google.com/web/u/0/conversations/CgjwUsdGGwxxGhIBNw", new PageGotoOptions { WaitUntil = WaitUntilState.Load }); - - var messageElement = msgPage.Locator("mws-text-message-part").Last; - string messageText = await messageElement.InnerTextAsync(); - - await originalPage.BringToFrontAsync(); - - var match = Regex.Match(messageText, @"\b\d{5,}\b"); - if (match.Success) - { - return match.Value; - } - else - { - Console.WriteLine("No OTP found in the message."); - return null; - } - } -} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 01c276a..d2d1821 100644 --- a/Program.cs +++ b/Program.cs @@ -1,6 +1,30 @@ -using BadmintonBooker; +using System.Text.RegularExpressions; +using BadmintonBooker; using Microsoft.Playwright; +var argsLength = args.Length; +if(argsLength < 1) +{ + Console.WriteLine("Useage: BadmintonBooker "); + return; +} +var endTime = args[0]; +if (!TimeSpan.TryParse(endTime, out var endTimeSpan)) +{ + Console.WriteLine("Invalid end time format. Use HH:mm"); + return; +} + +var debug = false; +if (argsLength == 2) +{ + if (!bool.TryParse(args[1], out debug)) + { + Console.WriteLine("Invalid debug value. Use true or false"); + return; + } +} + using var playwright = await Playwright.CreateAsync(); var context = await BrowserManager.GetContextAsync(playwright); var page = context.Pages.Count > 0 ? context.Pages[0] : await context.NewPageAsync(); @@ -15,7 +39,7 @@ await page.GetByRole(AriaRole.Button, new() { Name = "LOGIN" }).ClickAsync(); await page.GetByRole(AriaRole.Button, new() { Name = "CONTINUE" }).ClickAsync(new () { Delay = 1000 }); await Task.Delay(5000); -var egovOtp = await OTPExtractor.GetEgovOTP(context, page); +var egovOtp = await GoogleMessages.GetEgovOTP(context, page); if (egovOtp is not null) Console.WriteLine($"Extracted eGov OTP: {egovOtp}"); else @@ -29,4 +53,50 @@ await page.GetByText("Sports and Leisure").ClickAsync(); await page.GetByText(" Fahiveni Community Center ").First.ClickAsync(new () { Delay = 1500 }); await page.GetByText("Badminton Court 1").ClickAsync(new () { Delay = 1500 }); -Console.ReadLine(); \ No newline at end of file +var currentDateTime = DateTime.Now; +var upperBound = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day, + hour: 8, minute: 15, second: 0); + +var lowerBound = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day, + hour: 7, minute: 59, second: 59, millisecond: 800); + +var lastReloadTime = DateTime.Now; +while (!debug && (currentDateTime < lowerBound || currentDateTime >= upperBound)) +{ + if(lowerBound - currentDateTime > TimeSpan.FromSeconds(10) && + lastReloadTime - currentDateTime > TimeSpan.FromMinutes(2)) + { + await page.ReloadAsync(); + lastReloadTime = DateTime.Now; + } + + await Task.Delay(5); + currentDateTime = DateTime.Now; +} + +await page.Locator("div.flex.items-center.justify-end > div > div:nth-child(2) > svg").ClickAsync(); +await page.Locator("div.grid.bg-gray-100.rounded-lg.anim").Filter(new () { HasText = endTime }).ClickAsync(); +await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync(); +await page.GetByRole(AriaRole.Link, new() { Name = "Proceed to payment" }).ClickAsync(); +await page.GetByRole(AriaRole.Button, new() { Name = "Pay Now" }).ClickAsync(new () { Delay = 3500 }); +await page.GetByRole(AriaRole.Checkbox, new()).ClickAsync(); +await page.GetByRole(AriaRole.Button, new() { Name = "Pay now" }).ClickAsync(); +var cardDetails = File.ReadAllLines("CardDetails.txt"); +await page.GetByPlaceholder("Name on card").FillAsync(cardDetails[0]); +await page.GetByPlaceholder("1234 1234 1234 1234").FillAsync(cardDetails[1]); +await page.GetByPlaceholder("MM/YY").FillAsync(cardDetails[2]); +var cvv = File.ReadAllText("CVV.txt"); +await page.GetByPlaceholder("CVV").FillAsync(cvv); +await page.GetByText(new Regex("^Pay")).ClickAsync(new () { Delay = 1000 }); +await Task.Delay(15000); +var bmlOtp = await GoogleMessages.GetBMLOTP(context, page); +if (bmlOtp is not null) + Console.WriteLine($"Extracted BML OTP: {bmlOtp}"); +else + return; +await page.Locator("input[name='otpValue']").FillAsync(bmlOtp); +await page.GetByRole(AriaRole.Button, new() { Name = "CONFIRM" }).ClickAsync(); +await Task.Delay(15000); + +var hdcMessage = await GoogleMessages.GetMessageFromHDC(context, page); +Console.WriteLine($"Message from HDC: {hdcMessage}"); \ No newline at end of file diff --git a/README.md b/README.md index 0e03ae1..5f37807 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,10 @@ Helps me book the badminton court from HDC automatically. Right now now configuration is added but password's been removed from the repo and I will need to create a file called "Password.txt" to work on the project. -In the future perhaps it would be much better if I can deploy as a webapp that can be used by friends to schedule an auto purchase of a slot at a perticular time. \ No newline at end of file +In the future perhaps it would be much better if I can deploy as a webapp that can be used by friends to schedule an auto purchase of a slot at a perticular time. + +## Example Useage +**`dotnet run -- 22:00`** +Where `22:00` is the end time of the prefered time. `22:00` is the slot from `21:00 - 22:00`. + +Preferably the application will be run close to 8AM around 7:45AM so that there's no need to \ No newline at end of file