Skip to content

Commit

Permalink
πŸ”¨ WIP - visual diff notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Owen Melbourne committed Oct 6, 2020
1 parent 36efe12 commit 4fa823e
Show file tree
Hide file tree
Showing 16 changed files with 958 additions and 19 deletions.
128 changes: 128 additions & 0 deletions app/Checkers/VisualDiff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace App\Checkers;

use Exception;
use Ramsey\Uuid\Uuid;
use App\VisualDiff as Model;
use Spatie\Image\Manipulations;
use Spatie\Browsershot\Browsershot;
use App\Website;
use SebastianBergmann\Diff\Differ;
use Intervention\Image\Facades\Image;
use App\Notifications\RobotsHasChanged;
use Illuminate\Support\Facades\Storage;
use App\Notifications\VisualDifferenceFound;

class VisualDiff
{
private $website;

private $scan;

private $url;

private $diff;

public function __construct(Website $website, $url)
{
$this->website = $website;
$this->url = $url;
}

public function run()
{
$this->fetch();
$this->compare();
$this->notify();
}

private function fetch()
{
$filename = (string) Uuid::uuid4() . '.png';

// $this->scan = new Model([
// 'url' => $this->url,
//// 'screenshot' => $filename,
// 'screenshot' => '80df5ce1-0edf-46e3-a222-090069cb6a48.png',
// ]);

$this->scan = $this->website->visualDiffs()
->latest()
->where('url', $this->url)
->first();

try {
// Browsershot::url($this->url)
// ->windowSize(1440, 1024)
// ->fullPage()
// ->waitUntilNetworkIdle()
// ->setDelay(1000)
// ->save(
// Storage::disk('screenshots')->path($filename)
// );

// $this->website->visualDiffs()->save($this->scan);
} catch (Exception $exception) {
if (app()->environment('dev')) {
throw $exception;
}
}
}

private function compare()
{
$lastScan = $this->website->visualDiffs()
->latest()
->where('id', '!=', $this->scan->id)
->where('url', $this->url)
->first();

if (!$lastScan) {
return;
}

if ($lastScan->image->getHeight() > $this->scan->image->getHeight()) {
$this->scan->image->resizeCanvas(null, $lastScan->image->getHeight(), 'top-left')->save();
} else {
$lastScan->image->resizeCanvas(null, $this->scan->image->getHeight(), 'top-left')->save();
}

$differ = \BeyondCode\VisualDiff\VisualDiff::diff(
$lastScan->full_screenshot_path,
$this->scan->full_screenshot_path
);

$this->scan->diff_path = 'diff-' . $this->scan->id . '-' . $lastScan->id . '.png';
$this->scan->compared_with = $lastScan->id;

try {
$diff = $differ->save(
Storage::disk('screenshots')->path($this->scan->diff_path)
);

$this->scan->diff_found = data_get($diff, 'pixels', 0) > 10;
} catch (Exception $exception) {
if (app()->environment('dev')) {
throw $exception;
}
}

$this->scan->save();
}

private function notify()
{
if (!$this->scan) {
return null;
}

if (empty($this->scan->diff_found)) {
return null;
}

$this->website->user->notify(
new VisualDifferenceFound($this->website, $this->scan)
);
}
}
52 changes: 52 additions & 0 deletions app/Console/Commands/VisualDiffCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Console\Commands;

use App\Website;
use App\Jobs\VisualDiffCheck;
use Illuminate\Console\Command;

class VisualDiffCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:visual-diff {website} {--force}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Will run the visual diff check for a single website.';

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}

/**
* Execute the console command.
*/
public function handle()
{
$forced = $this->option('force', false);
$websiteId = $this->argument('website');
$website = Website::findOrFail($websiteId);

if (!$website->visual_diff_enabled && !$forced) {
return $this->error('Visual diffs disabled for ' . $website->url . ', use --force to force a check.');
}

$website->visual_urls_to_scan->each(function ($url) use ($website) {
VisualDiffCheck::dispatchNow($website, $url);
});
}
}
26 changes: 26 additions & 0 deletions app/HasVisualDiffs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App;

trait HasVisualDiffs
{
public function visualDiffs()
{
return $this->hasMany(VisualDiff::class);
}

public function getLastVisualDiffsAttribute()
{
return $this->visualDiffs()->orderBy('created_at', 'desc')->take(2)->get();
}

public function getVisualUrlsToScanAttribute()
{
return collect(explode("\n", $this->visual_diff_urls))->map(function ($url) {
return trim($url);
})->filter(function ($url) {
return filter_var($url, FILTER_VALIDATE_URL);
})->values();
}

}
55 changes: 55 additions & 0 deletions app/Jobs/VisualDiffCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace App\Jobs;

use App\Website;
use App\Checkers\Robots;
use App\Checkers\VisualDiff;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class VisualDiffCheck implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* @var Website
*/
private $website;

private $url;

/**
* Create a new job instance.
*
* @param Website $website
* @param string $url
*/
public function __construct(Website $website, string $url)
{
$this->website = $website;
$this->url = $url;
}

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$checker = new VisualDiff($this->website, $this->url);
$checker->run();
}

public function tags()
{
return [
static::class,
'Website:' . $this->website->id,
];
}
}
77 changes: 77 additions & 0 deletions app/Notifications/VisualDifferenceFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace App\Notifications;

use App\Website;
use App\VisualDiff;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;

class VisualDifferenceFound extends Notification
{
use Queueable;

/**
* @var Website
*/
private $website;

/**
* @var VisualDiff
*/
private $scan;

/**
* Create a new notification instance.
*
* @param Website $website
* @param VisualDiff $scan
*/
public function __construct(Website $website, VisualDiff $scan)
{
$this->website = $website;
$this->scan = $scan;
}

/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['database', 'mail'];
}

/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('πŸŒ„ Visual Difference on: ' . $this->website->url)
->markdown('mail.visual-diff', [
'website' => $this->website,
'scan' => $this->scan,
]);
}

/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'website' => $this->website,
'scan' => $this->scan,
];
}
}
Loading

0 comments on commit 4fa823e

Please sign in to comment.