13 Commits

Author SHA1 Message Date
17af60471b Fix Notifications
Some checks failed
Push image to registry / build-image (push) Failing after 4m19s
2025-03-02 13:31:26 +01:00
0014045d52 Fix job 2025-03-02 13:29:44 +01:00
fec63ff249 Added Debug notifications
Some checks failed
Push image to registry / build-image (push) Failing after 3m41s
2025-03-02 10:03:27 +01:00
cdcdcd9e8b Wait for daily free if less than a minute
Some checks failed
Push image to registry / build-image (push) Failing after 3m45s
2025-03-02 09:40:07 +01:00
599ef77d64 Should fix steam connexion
Some checks failed
Push image to registry / build-image (push) Failing after 6m50s
2025-03-02 09:08:44 +01:00
edb07dd960 Upgrade to Laravel 12
Some checks failed
Push image to registry / build-image (push) Failing after 3m42s
2025-03-01 15:31:11 +01:00
6a95653c52 Added notification on job fail
All checks were successful
Push image to registry / build-image (push) Successful in 5m20s
2025-03-01 15:12:15 +01:00
025711e09d Updated dockerignore
All checks were successful
Push image to registry / build-image (push) Successful in 5m1s
2025-03-01 12:49:02 +01:00
5a30cdeae5 Made a script to patch latest selenium/standalone-chromedriver 2025-03-01 12:33:03 +01:00
db9d65f445 Fix job ordering 2025-03-01 10:02:47 +01:00
a1219b92ce Should fix
All checks were successful
Push image to registry / build-image (push) Successful in 7m31s
2025-02-28 17:05:47 +01:00
ce13d1b0dd Job qui enlève les vieilles jobRun
All checks were successful
Push image to registry / build-image (push) Successful in 6m18s
2025-02-27 18:54:40 +01:00
a80a32eee8 Added links to browser downloads and others
All checks were successful
Push image to registry / build-image (push) Successful in 6m20s
2025-02-27 17:57:54 +01:00
30 changed files with 364 additions and 24 deletions

View File

@ -14,6 +14,7 @@ database/database.sqlite
**/.dockerignore
**/.env
**/.git
.gitea/
**/.gitignore
**/.project
**/.settings
@ -67,3 +68,5 @@ yarn-error.log
app/Browser/console/**
app/Browser/screenshots/**
app/Browser/source/**
undetectedChromedriver

5
.gitignore vendored
View File

@ -21,6 +21,11 @@ yarn-error.log
/.nova
/.vscode
/.zed
# Python projet
venv
__pycache__
# Browser
app/Browser/console
app/Browser/screenshots

View File

@ -5,6 +5,8 @@ namespace App\Browser;
use App\Models\Job;
use App\Models\JobArtifact;
use App\Models\JobRun;
use App\Notification\Notifications\JobErrorNotification;
use App\Notification\Providers\AllNotification;
use Closure;
use Exception;
use Facebook\WebDriver\Chrome\ChromeOptions;
@ -56,7 +58,8 @@ abstract class BrowserJob implements ShouldQueue
// throw $e;
}
catch (Throwable $e) {
$browser->screenshot("failure-{$this->jobId}");
$browser->screenshot(JobErrorScreenshot::getFileName($this->jobId));
AllNotification::send(new JobErrorNotification($this->jobId, $e->getMessage()));
dump($e);
throw $e;
} finally {

View File

@ -0,0 +1,27 @@
<?php
namespace App\Browser;
use Laravel\Dusk\Browser;
use function rtrim;
class JobDebugScreenshot {
public const IMG_FILE_NAME = "debug-";
public static function getFileName(int $jobId): string {
return static::IMG_FILE_NAME . $jobId . ".png";
}
public static function getImgFileAbsolutePath(int $jobId): string {
return rtrim(Browser::$storeScreenshotsAt, '/') . "/" . static::getFileName($jobId);
}
public static function getImgFileProjectPath(int $jobId): string {
return app_path("Browser/screenshots/" . static::getFileName($jobId));
}
public static function getImgFileExternalPath(int $jobId): string {
return "screenshots/" . static::getFileName($jobId);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Browser;
use Laravel\Dusk\Browser;
use function rtrim;
class JobErrorScreenshot {
public const IMG_FILE_NAME = "failure-";
public static function getFileName(int $jobId): string {
return static::IMG_FILE_NAME . $jobId . ".png";
}
public static function getImgFileAbsolutePath(int $jobId): string {
return rtrim(Browser::$storeScreenshotsAt, '/') . "/" . static::getFileName($jobId);
}
public static function getImgFileProjectPath(int $jobId): string {
return app_path("Browser/screenshots/" . static::getFileName($jobId));
}
public static function getImgFileExternalPath(int $jobId): string {
return "screenshots/" . static::getFileName($jobId);
}
}

View File

@ -4,6 +4,7 @@ namespace App\Browser\Jobs\Hellcase;
use App\Browser\BrowserJob;
use App\Browser\Components\Hellcase\MainNav;
use App\Browser\JobDebugScreenshot;
use App\Browser\Jobs\Hellcase\HellcaseLoginQrCode;
use App\Models\JobArtifact;
use App\Models\JobRun;
@ -11,6 +12,8 @@ use App\Notification\NotificationBody\Hellcase\HellcaseNotificationDailyFreeBody
use App\Notification\NotificationBody\Hellcase\HellcaseNotificationLoginBody;
use App\Notification\Notifications\Hellcase\HellcaseNotificationDailyFree;
use App\Notification\Notifications\Hellcase\HellcaseNotificationLogin;
use App\Notification\Notifications\JobDebugNotification;
use App\Notification\Notifications\JobErrorNotification;
use App\Notification\Providers\AllNotification;
use Facebook\WebDriver\WebDriverBy;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
@ -40,7 +43,7 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
$this->jobRun->save();
$browser->visit('https://hellcase.com');
$browser->waitForText("Store", 30, true);
$browser->waitForText("CASES", 30, true);
$this->removePopups($browser);
sleep(5);
$this->signin($browser);
@ -97,7 +100,14 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
// QR CODE SCANNING
try {
$browser->waitForTextIn("div", "Or sign in with QR", 30, true);
$qrCode = $browser->driver->findElement(WebDriverBy::xpath('//div[./*[contains(text(), "Or sign in with QR")]]'));
sleep(10);
try {
$qrCode = $browser->driver->findElement(WebDriverBy::xpath('//div[./*[contains(text(), "Or sign in with QR")]]'));
} catch (\Exception $e) {
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "Le QR code de la page de connexion de Steam n'a pas été trouvé"));
throw $e;
}
// Wait to be redirected to the Steam login page, while waiting take a new screenshot every 30 seconds
$isBackOnHellcase = false;
@ -122,9 +132,11 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
}
} catch (\Exception $e) {
// If the QR code is not found, we are not on the QR code page
Log::debug("Exception because qrcode not found : " . $e);
$isBackOnHellcase = true;
} catch (\Throwable $e) {
// If the QR code is not found, we are not on the QR code page
Log::debug("Exception because qrcode not found : " . $e);
$isBackOnHellcase = true;
}
@ -196,9 +208,12 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
if ($availibleInButton->getAttribute("disabled") == "true") {
$hours = $availibleInButton->getText();
// If the text is like "in 26 sec." we need to put one minute
if (str_contains($hours, "sec")) {
$this->reschedule(1);
return;
if (str_contains(strtolower($hours), "seconds")) {
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "I hate niggers"));
// $this->reschedule(1);
sleep(60);
return $this->getDailyFree($browser);
}
$hours = explode(" ", $hours);
$minutes = $hours[4];

View File

@ -0,0 +1,35 @@
<?php
namespace App\Jobs;
use App\Models\Job;
use App\Models\JobRun;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class PruneOldJobRuns implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
// For each job, keep only the last N runs
foreach (Job::all() as $job) {
$job->jobRuns()
->orderByDesc('id')
->skip(config('jobs.pruneOldJobRuns.max_runs_per_job'))
->delete();
}
}
}

View File

@ -4,6 +4,7 @@ namespace App\Notification;
use App\Models\Job;
use App\Notification\Stringifiable\StringifiableSimpleText;
use function PHPUnit\Framework\isNull;
abstract class Notification {
@ -12,12 +13,18 @@ abstract class Notification {
public bool $isError;
public function __construct(int $jobId, NotificationBody $body, bool $isError = false) {
public function __construct(int $jobId, NotificationBody $body = null, bool $isError = false) {
$this->job = Job::find($jobId);
$this->body = $body;
if ($body !== null) {
$this->body = $body;
}
$this->isError = $isError;
}
public function setBody(NotificationBody $body) {
$this->body = $body;
}
public function getTitle(): Stringifiable {
return new StringifiableSimpleText($this->job->name);
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Notification\NotificationBody;
use App\Models\Job;
use App\Notification\NotificationBody;
use App\Notification\Stringifiable;
class JobDebugNotificationBody extends NotificationBody {
private Job $job;
private string $body;
private ?string $error;
private bool $hasScreenshot;
public function __construct(Job $job, string $body, string $error = null, bool $hasScreenshot = false) {
$this->job = $job;
$this->body = $body;
$this->error = $error;
$this->hasScreenshot = $hasScreenshot;
}
private function constructString(bool $inMarkdown = false) {
$mdBody = "";
if ($this->body !== null) {
$mdBody .= $this->body;
}
if ($this->error !== null) {
$errorWrapper = $inMarkdown ? "```" : "";
$mdBody .= " :\n" . $errorWrapper . $this->error . $errorWrapper;
}
if ($inMarkdown && $this->hasScreenshot) {
$mdBody .= "\nScreenshot : ";
}
return $mdBody;
}
/**
* @inheritDoc
*/
public function toMarkdownString(): string {
return $this->constructString(true);
}
/**
* @inheritDoc
*/
public function toString(): string {
return $this->constructString();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Notification\NotificationBody;
use App\Models\Job;
use App\Notification\NotificationBody;
use App\Notification\Stringifiable;
class JobErrorNotificationBody extends NotificationBody {
private Job $job;
private string $error;
public function __construct(Job $job, $error) {
$this->job = $job;
$this->error = $error;
}
/**
* @inheritDoc
*/
public function toMarkdownString(): string {
return "Le job \"{$this->job->name}\" a échoué avec l'erreur :\n ```" . $this->error . "``` \nScreenshot : ";
}
/**
* @inheritDoc
*/
public function toString(): string {
return "Le job \"{$this->job->name}\" a échoué avec l'erreur :\n " . $this->error;
}
}

View File

@ -3,12 +3,9 @@
namespace App\Notification\Notifications\Hellcase;
use App\Browser\Jobs\Hellcase\HellcaseDailyFreeScreenshot;
use App\Browser\Jobs\Hellcase\HellcaseLoginQrCode;
use App\Notification\Notification;
use App\Notification\Notifications\NotificationLogin;
use Laravel\Dusk\Browser;
class HellcaseNotificationDailyFree extends NotificationLogin {
class HellcaseNotificationDailyFree extends Notification {
public function __construct(int $jobId, \App\Notification\NotificationBody $body) {
parent::__construct($jobId, $body);

View File

@ -0,0 +1,44 @@
<?php
namespace App\Notification\Notifications;
use App\Browser\JobDebugScreenshot;
use App\Notification\Notification;
use App\Notification\NotificationBody\JobDebugNotificationBody;
use App\Notification\Stringifiable;
use App\Notification\Stringifiable\StringifiableSimpleText;
use Illuminate\Support\Facades\Log;
class JobDebugNotification extends Notification {
private string|null $title;
private string|null $screenShotProjectPath;
public function __construct(int $jobId, string $body, string $title = null, string $error = null, ?string $screenshotProjectPath = "", bool $isError = false) {
parent::__construct($jobId, isError:$isError);
$this->title = $title;
if ($screenshotProjectPath === "") {
$screenshotProjectPath = JobDebugScreenshot::getImgFileProjectPath($jobId);
}
$this->screenShotProjectPath = $screenshotProjectPath;
$this->setBody(new JobDebugNotificationBody($this->job, $body, $error, $this->screenShotProjectPath != null));
}
public function getTitle(): Stringifiable {
return new StringifiableSimpleText($this->title ?? "DEBUG Job {$this->job->name}");
}
/**
* @inheritDoc
*/
public function getImageProjectPath(): string|null {
return $this->screenShotProjectPath;
}
/**
* @inheritDoc
*/
public function getLinkURL(): string|null {
return route('jobs.show', ['job' => $this->job->id]);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Notification\Notifications;
use App\Browser\JobErrorScreenshot;
use App\Models\Job;
use App\Notification\Notification;
use App\Notification\NotificationBody\JobErrorNotificationBody;
use App\Notification\Stringifiable;
use App\Notification\Stringifiable\StringifiableSimpleText;
use Illuminate\Support\Facades\Log;
class JobErrorNotification extends Notification {
public function __construct(int $jobId, string $error) {
parent::__construct($jobId, isError:true);
$this->setBody(new JobErrorNotificationBody($this->job, $error));
}
public function getTitle(): Stringifiable {
return new StringifiableSimpleText("Le job {$this->job->name} a échoué");
}
/**
* @inheritDoc
*/
public function getImageProjectPath(): string|null {
return JobErrorScreenshot::getImgFileProjectPath($this->job->id);
}
/**
* @inheritDoc
*/
public function getLinkURL(): string|null {
return route('jobs.show', ['job' => $this->job->id]);
}
}

View File

@ -3,9 +3,6 @@
namespace App\Notification\Providers;
use App\Notification\NotificationProvider;
use App\Notification\INotificationProvider;
use App\Models\JobInfo;
use Illuminate\Support\Facades\Cache;
class AllNotification extends NotificationProvider {
private const NOTIFICATIONS_PROVIDERS = [

View File

@ -3,7 +3,6 @@
namespace App\Notification\Providers;
use App\Notification\NotificationProvider;
use App\Notification\INotificationProvider;
use App\Models\JobInfo;
use Illuminate\Support\Facades\Cache;

View File

@ -13,7 +13,7 @@
"erusev/parsedown": "^1.7",
"inertiajs/inertia-laravel": "^2.0",
"laravel/dusk": "^8.2",
"laravel/framework": "^11.31",
"laravel/framework": "^12.0",
"laravel/reverb": "^1.0",
"laravel/sanctum": "^4.0",
"laravel/telescope": "^5.5",

View File

@ -75,6 +75,10 @@ return [
'links' => [
public_path('storage') => storage_path('app/public'),
public_path('console') => base_path('app/Browser/console'),
public_path('downloads') => base_path('app/Browser/downloads'),
public_path('screenshots') => base_path('app/Browser/screenshots'),
public_path('source') => base_path('app/Browser/source'),
],
];

20
config/jobs.php Normal file
View File

@ -0,0 +1,20 @@
<?php
use Laravel\Telescope\Http\Middleware\Authorize;
use Laravel\Telescope\Watchers;
return [
/**
* Remove old job runs from the database.
*/
'pruneOldJobRuns' => [
'enabled' => true,
/**
* How many job runs a job can keep before we start pruning old ones.
*/
'max_runs_per_job' => 50,
],
];

1
public/console Symbolic link
View File

@ -0,0 +1 @@
/home/ninluc/Documents/codage/DatBrowser/app/Browser/console

1
public/downloads Symbolic link
View File

@ -0,0 +1 @@
/home/ninluc/Documents/codage/DatBrowser/app/Browser/downloads

1
public/screenshots Symbolic link
View File

@ -0,0 +1 @@
/home/ninluc/Documents/codage/DatBrowser/app/Browser/screenshots

1
public/source Symbolic link
View File

@ -0,0 +1 @@
/home/ninluc/Documents/codage/DatBrowser/app/Browser/source

View File

@ -11,7 +11,7 @@ const jobs = ref<Job[]>([]);
async function fetchJobs() {
let jobsRaw = await httpApi<Job[]>("/jobs");
jobs.value = jobsRaw.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
jobs.value = jobsRaw.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
}
onMounted(fetchJobs);

View File

@ -1,7 +1,7 @@
<?php
use App\Browser\Jobs\Hellcase\HellcaseJob;
use App\Models\Job;
use App\Jobs\PruneOldJobRuns;
use App\Services\BrowserJobsInstances;
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
@ -13,6 +13,11 @@ Artisan::command('inspire', function () {
// Telescope
Schedule::command('telescope:prune')->monthly();
// Prune old job runs
Schedule::job(new PruneOldJobRuns)->monthly()->onOneServer()->withoutOverlapping()->name('prune-old-job-runs')->description('Prune old job runs')->skip(function () {
return !config('jobs.pruneOldJobRuns.enabled');
});
// Jobs
Schedule::job(new HellcaseJob)->daily()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
// Schedule::job(new HellcaseJob)->everyMinute()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');

View File

@ -1,16 +1,17 @@
# TODO
- Fix hellcase, fermer lespopups à chaque nouvelle visite
- Voir si le scheduler fonctionne au démmarage
- Mettre un timeout pour pas overwhelm le pc au démmarage
- Image pour le join de giveaway
- → Notification
- Ou ajouter des images base64 dans les jobRun
- Risque de devenir volumineux
- Sauf avec le jobqui enlève les vieilles jobRun
- Sauf avec le job qui enlève les vieilles jobRun
- Notification live websocket
- Websocket installé
- Serveur php plus propre (nginx, apache, n'importe)
- Job qui supprime les vieilles JobRun
- Epic games
## Pour deploy Lama

Binary file not shown.

View File

@ -1 +1,20 @@
sudo docker run --rm -it -p 3389:3389 -v ./undetectedChromedriver:/root/.local/share/undetected_chromedriver/ ultrafunk/undetected-chromedriver:latest
#!/bin/bash
# From undetected chromedriver docker
#sudo docker run --rm -it -p 3389:3389 -v ./undetectedChromedriver:/root/.local/share/undetected_chromedriver/ ultrafunk/undetected-chromedriver:latest
# With undetected chromedriver patcher
# Run the selenium/standalone-chrome:latest with a specific container name in the background
sudo docker run -d --name standalone-chrome selenium/standalone-chrome:latest
sleep 5
# Copy the chromedriver binary from the container to the host
sudo docker cp -L standalone-chrome:/bin/chromedriver ./chromedriver
# Stop the container
sudo docker stop standalone-chrome
sudo chmod 777 ./chromedriver
# Patch the chromedriver binary
python3 ./patchChromedriver.py

View File

@ -0,0 +1,8 @@
#!/bin/python3
import undetected_chromedriver as uc
options = uc.ChromeOptions()
# Chromedriver is in current directory
driver = uc.Chrome(options = options, browser_executable_path="/usr/bin/google-chrome", driver_executable_path="/home/ninluc/Documents/codage/DatBrowser/undetectedChromedriver/chromedriver")
driver.get('https://nowsecure.nl')

View File

@ -1,6 +1,7 @@
FROM selenium/standalone-chrome:108.0 AS final
# FROM selenium/standalone-chrome:108.0 AS final
FROM selenium/standalone-chrome:latest AS final
COPY undetectedChromedriver/chromedriver-linux /bin/chromedriver
COPY undetectedChromedriver/chromedriver /bin/chromedriver
RUN mkdir -p /home/seluser/profile/
ENV TZ=Europe/Brussels