Completed Payment and Timings (not tested during 8AM timing yet.)

This commit is contained in:
2026-04-12 11:02:42 +05:00
parent 430c5fd2b8
commit 869b0b466e
6 changed files with 147 additions and 33 deletions
+2
View File
@@ -1,4 +1,6 @@
[Pp]assword.txt [Pp]assword.txt
CVV.txt
CardDetails.txt
# ---> VisualStudioCode # ---> VisualStudioCode
.vscode/* .vscode/*
+2
View File
@@ -14,6 +14,8 @@
<ItemGroup> <ItemGroup>
<None Include="Password.txt" CopyToOutputDirectory="PreserveNewest" /> <None Include="Password.txt" CopyToOutputDirectory="PreserveNewest" />
<None Include="CardDetails.txt" CopyToOutputDirectory="PreserveNewest" />
<None Include="CVV.txt" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+63
View File
@@ -0,0 +1,63 @@
using System.Text.RegularExpressions;
using Microsoft.Playwright;
namespace BadmintonBooker;
public static class GoogleMessages
{
public static async Task<string?> 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<string?> 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<string> 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;
}
}
-29
View File
@@ -1,29 +0,0 @@
using System.Text.RegularExpressions;
using Microsoft.Playwright;
namespace BadmintonBooker;
public static class OTPExtractor
{
public static async Task<string?> 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;
}
}
}
+73 -3
View File
@@ -1,6 +1,30 @@
using BadmintonBooker; using System.Text.RegularExpressions;
using BadmintonBooker;
using Microsoft.Playwright; using Microsoft.Playwright;
var argsLength = args.Length;
if(argsLength < 1)
{
Console.WriteLine("Useage: BadmintonBooker <endtime (HH:mm)> <debug? (true/false)>");
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(); using var playwright = await Playwright.CreateAsync();
var context = await BrowserManager.GetContextAsync(playwright); var context = await BrowserManager.GetContextAsync(playwright);
var page = context.Pages.Count > 0 ? context.Pages[0] : await context.NewPageAsync(); 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 page.GetByRole(AriaRole.Button, new() { Name = "CONTINUE" }).ClickAsync(new () { Delay = 1000 });
await Task.Delay(5000); await Task.Delay(5000);
var egovOtp = await OTPExtractor.GetEgovOTP(context, page); var egovOtp = await GoogleMessages.GetEgovOTP(context, page);
if (egovOtp is not null) if (egovOtp is not null)
Console.WriteLine($"Extracted eGov OTP: {egovOtp}"); Console.WriteLine($"Extracted eGov OTP: {egovOtp}");
else 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(" Fahiveni Community Center ").First.ClickAsync(new () { Delay = 1500 });
await page.GetByText("Badminton Court 1").ClickAsync(new () { Delay = 1500 }); await page.GetByText("Badminton Court 1").ClickAsync(new () { Delay = 1500 });
Console.ReadLine(); 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}");
+6
View File
@@ -3,3 +3,9 @@
Helps me book the badminton court from HDC automatically. 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. 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. 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