diff --git a/src/Extractors/Banlist/BlueExtractor.php b/src/Extractors/Banlist/BlueExtractor.php new file mode 100644 index 0000000..e0d85a5 --- /dev/null +++ b/src/Extractors/Banlist/BlueExtractor.php @@ -0,0 +1,75 @@ +filter('head link[rel=stylesheet]')->each(fn (Crawler $link) => $link->attr('href')); + + return collect($css)->contains( + fn (string $href) => str_starts_with(trim($href, '/'), 'themes/sourcebans_blue/css') + ); + } + + public function handle(Crawler $crawler): ?LengthAwarePaginator + { + $tables = $crawler->filter('#banlist table.listtable tr td div.opener table.listtable'); + + if ($tables->count() === 0) { + return null; + } + + $bans = $tables->each(function (Crawler $table): ?Ban { + $data = new Fluent(); + + $table->filter('tr')->each(function (Crawler $row) use ($data): void { + $cells = $row->filter('td.listtable_1'); + + if ($cells->count() !== 2) { + return; + } + + $key = Str::slug($cells->eq(0)->innerText(), '_'); + $value = rescue(fn () => $cells->eq(1)->innerText(), report: false); + + $data->{$key} = $value; + }); + + return rescue( + callback: fn () => new Ban( + steam_id: new SteamID($data->steam_id), + invoked_on: $this->toCarbonImmutable($data->invoked_on), + ban_length: $this->toCarbonInterval($data->ban_length ?? $data->banlength), + expires_on: $this->toCarbonImmutable($data->expires_on), + ban_reason: $data->reason ?: null, + unban_reason: $data->unban_reason ?: null, + total_bans: (int) $data->total_bans, + ), + report: false + ); + }); + + $pagination = $this->paginationFromString( + $crawler->filter('#banlist-nav')->innerText() + ); + + return new LengthAwarePaginator( + items: collect($bans) + ->filter() + ->values(), + total: $pagination['total'], + perPage: $pagination['end'] - $pagination['start'], + currentPage: $this->currentPageFromSelect($crawler->filter('#banlist-nav select')), + ); + } +} diff --git a/src/Extractors/Extractor.php b/src/Extractors/Extractor.php index 30e6dbe..9dadd21 100644 --- a/src/Extractors/Extractor.php +++ b/src/Extractors/Extractor.php @@ -28,7 +28,7 @@ protected function toCarbonImmutable(?string $value): ?CarbonImmutable 'd.m.Y H:i', 'd.m.y H:i:s', 'l dS \o\f F Y h:i:s A', - 'd-m-Y | H:i:s', + 'd-m-Y \| H:i:s', ]) ->map(fn (string $format) => rescue(fn () => CarbonImmutable::createFromFormat($format, $value), report: false)) ->filter() diff --git a/src/SourceBansSdkServiceProvider.php b/src/SourceBansSdkServiceProvider.php index 5e57822..92be5d4 100644 --- a/src/SourceBansSdkServiceProvider.php +++ b/src/SourceBansSdkServiceProvider.php @@ -2,6 +2,7 @@ namespace Astrotomic\SourceBansSdk; +use Astrotomic\SourceBansSdk\Extractors\Banlist\BlueExtractor; use Astrotomic\SourceBansSdk\Extractors\Banlist\DefaultExtractor; use Astrotomic\SourceBansSdk\Extractors\Banlist\FluentExtractor; use Illuminate\Support\ServiceProvider; @@ -12,10 +13,12 @@ public function register(): void { $this->app->bind(DefaultExtractor::class); $this->app->bind(FluentExtractor::class); + $this->app->bind(BlueExtractor::class); $this->app->tag([ DefaultExtractor::class, FluentExtractor::class, + BlueExtractor::class, ], 'extractors.banlist'); } } diff --git a/tests/Datasets/baseurls.php b/tests/Datasets/baseurls.php index 1c2163d..557b673 100644 --- a/tests/Datasets/baseurls.php +++ b/tests/Datasets/baseurls.php @@ -3,7 +3,7 @@ dataset('baseurls', [ // default 'https://firepoweredgaming.com/sourcebanspp/index.php', - 'https://bans.neonheightsservers.ca/index.php', + 'https://neonheights.xyz/bans/index.php', 'https://bans.harpoongaming.com/index.php', 'https://bans.panda-community.com/index.php', 'https://sourcebans.gamerzhost.de/sb419/index.php', @@ -20,11 +20,9 @@ 'http://162.55.184.155/index.php', 'https://sourcebans.ghostcap.com/index.php', 'https://pong.setti.info/sourcebans/index.php', - 'http://freiheit-servers.ru/bans/index.php', // fluent 'https://www.cswroclaw.pl/sbcsw/index.php', 'https://bans.zzk-community.de/index.php', - 'https://kitiltasok.vrain.hu/index.php', 'https://sourcebans.zkservidores.com/index.php', 'https://www.sirplease.gg/index.php', 'https://sourcebans.onetap.pl/index.php', @@ -32,6 +30,5 @@ 'https://sb.feelthegame.eu/index.php', 'https://goodteemo.serverscstrike.com/index.php', 'https://bans.gaming-biatch.eu/index.php', - 'https://bans.pineriver.dk/index.php', 'https://newlandscsgo.com.br/sourcebans/index.php', ]); diff --git a/tests/Feature/Requests/QueryBansRequestTest.php b/tests/Feature/Requests/QueryBansRequestTest.php index c2468a1..c7dca8e 100644 --- a/tests/Feature/Requests/QueryBansRequestTest.php +++ b/tests/Feature/Requests/QueryBansRequestTest.php @@ -9,7 +9,7 @@ it('can load first page of bans', function (string $baseUrl): void { $bans = $this->sourcebans($baseUrl)->queryBans(page: 1); - Assert::assertGreaterThanOrEqual(0, $bans->count()); + Assert::assertGreaterThan(0, $bans->count()); Assert::assertLessThanOrEqual($bans->perPage(), $bans->count()); Assert::assertContainsOnlyInstancesOf(Ban::class, $bans); })->with('baseurls'); @@ -34,10 +34,10 @@ it('can load all bans', function (string $baseUrl): void { $bans = $this->sourcebans($baseUrl)->queryBans(); - Assert::assertGreaterThanOrEqual(0, $bans->count()); + Assert::assertGreaterThan(0, $bans->count()); Assert::assertContainsOnlyInstancesOf(Ban::class, $bans); })->with([ - 'http://freiheit-servers.ru/bans/index.php', // default + 'https://swedisholdtimers.net/sourcebans/index.php', // default 'https://www.weallplay.eu/sourcebans/index.php', // fluent ]); diff --git a/tests/Fixtures/Saloon/bans.neonheightsservers.ca/GET/index.php/p=banlist&page=1.json b/tests/Fixtures/Saloon/bans.neonheightsservers.ca/GET/index.php/p=banlist&page=1.json deleted file mode 100644 index afc4fb6..0000000 --- a/tests/Fixtures/Saloon/bans.neonheightsservers.ca/GET/index.php/p=banlist&page=1.json +++ /dev/null @@ -1 +0,0 @@ -{"statusCode":200,"headers":{"Date":["Thu, 17 Nov 2022 17:38:20 GMT"],"Server":["Apache"],"X-Powered-By":["PHP\/7.4.28"],"Set-Cookie":["SourceBans_Session=749jdc6bnr8ks101j4o3ej4cqm; expires=Fri, 18-Nov-2022 17:38:20 GMT; Max-Age=86400; path=\/; domain=bans.neonheightsservers.ca; HttpOnly","SourceBans_Session=1cqpiug3u289cm4sqmvvtjjha8; expires=Fri, 18-Nov-2022 17:38:20 GMT; Max-Age=86400; path=\/; domain=bans.neonheightsservers.ca; HttpOnly","SourceBans_Session=1cqpiug3u289cm4sqmvvtjjha8; expires=Fri, 18-Nov-2022 17:38:20 GMT; Max-Age=86400; path=\/; domain=bans.neonheightsservers.ca; HttpOnly"],"Expires":["Thu, 19 Nov 1981 08:52:00 GMT"],"Cache-Control":["no-store, no-cache, must-revalidate"],"Pragma":["no-cache"],"Vary":["Accept-Encoding"],"Transfer-Encoding":["chunked"],"Content-Type":["text\/html; charset=UTF-8"]},"data":"\n\n
\n\n