diff --git a/BinDays.Api.Collectors/Collectors/Councils/BroxtoweBoroughCouncil.cs b/BinDays.Api.Collectors/Collectors/Councils/BroxtoweBoroughCouncil.cs new file mode 100644 index 0000000..1347d0e --- /dev/null +++ b/BinDays.Api.Collectors/Collectors/Councils/BroxtoweBoroughCouncil.cs @@ -0,0 +1,412 @@ +namespace BinDays.Api.Collectors.Collectors.Councils; + +using BinDays.Api.Collectors.Collectors.Vendors; +using BinDays.Api.Collectors.Models; +using BinDays.Api.Collectors.Utilities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; + +/// +/// Collector implementation for Broxtowe Borough Council. +/// +internal sealed partial class BroxtoweBoroughCouncil : GovUkCollectorBase, ICollector +{ + /// + public string Name => "Broxtowe Borough Council"; + + /// + public Uri WebsiteUrl => new("https://www.broxtowe.gov.uk/"); + + /// + public override string GovUkId => "broxtowe"; + + /// + /// The list of bin types for this collector. + /// + private readonly IReadOnlyCollection _binTypes = + [ + new() + { + Name = "Mixed Dry Recycling", + Colour = BinColour.Green, + Keys = [ "GREEN 240L", ], + }, + new() + { + Name = "Glass Recycling", + Colour = BinColour.Green, + Keys = [ "GLASS BAG", ], + Type = BinType.Bag, + }, + new() + { + Name = "Garden Waste", + Colour = BinColour.Brown, + Keys = [ "BROWN 240L", ], + }, + new() + { + Name = "General Waste", + Colour = BinColour.Black, + Keys = [ "BLACK 240L", ], + }, + ]; + + /// + /// The URL of the web form for bin collection lookups. + /// + private const string _formUrl = "https://selfservice.broxtowe.gov.uk/renderform.aspx?t=217&k=9D2EF214E144EE796430597FB475C3892C43C528"; + + /// + /// The ASP.NET script manager target for AJAX requests. + /// + private const string _scriptManagerTarget = "ctl00$ContentPlaceHolder1$APUP_5683"; + + /// + /// The event target for postcode search button. + /// + private const string _searchEventTarget = "ctl00$ContentPlaceHolder1$FF5683BTN"; + + /// + /// The event target for address dropdown selection. + /// + private const string _addressEventTarget = "ctl00$ContentPlaceHolder1$FF5683DDL"; + + /// + /// Regex for parsing hidden fields from AJAX responses. + /// + [GeneratedRegex(@"hiddenField\|(?__VIEWSTATEGENERATOR|__EVENTVALIDATION|__VIEWSTATE)\|(?[^|]+)")] + private static partial Regex AjaxHiddenFieldRegex(); + + /// + /// Regex for parsing hidden fields from HTML responses. + /// + [GeneratedRegex(@"name=""(?__VIEWSTATEGENERATOR|__EVENTVALIDATION|__VIEWSTATE)""[^>]+value=""(?[^""]+)""")] + private static partial Regex HtmlHiddenFieldRegex(); + + /// + /// Regex for parsing address options. + /// + [GeneratedRegex(@"