Initial Commit
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Attributes\Description;
|
||||
use Illuminate\Console\Attributes\Signature;
|
||||
|
||||
#[Signature('app:debug-playwright')]
|
||||
#[Description('Command description')]
|
||||
class DebugPlaywright extends RunsPlaywrightScript
|
||||
{
|
||||
protected $signature = 'direct:debug';
|
||||
protected $description = 'Run the Playwright network/DOM debugging script against Instagram Direct inbox';
|
||||
protected string $script = 'debugger.spec.js';
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
|
||||
abstract class RunsPlaywrightScript extends Command
|
||||
{
|
||||
protected string $script;
|
||||
protected int $processTimeout = 300;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->info("Running {$this->script}...");
|
||||
|
||||
$result = Process::path(base_path('playwright'))
|
||||
->timeout($this->processTimeout)
|
||||
->run("npx playwright test {$this->script} --reporter=line");
|
||||
|
||||
$this->line($result->output());
|
||||
|
||||
if (! $result->successful()) {
|
||||
$this->error($result->errorOutput());
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->afterRun();
|
||||
|
||||
$this->info('Done.');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
protected function afterRun(): void
|
||||
{
|
||||
// no-op by default
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\DirectMessage;
|
||||
use App\Models\DirectThread;
|
||||
use Illuminate\Console\Attributes\Description;
|
||||
use Illuminate\Console\Attributes\Signature;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
#[Signature('direct:scrape-messages')]
|
||||
#[Description('Scrape Instagram Direct threads and messages via Playwright')]
|
||||
class ScrapeDirectMessages extends RunsPlaywrightScript
|
||||
{
|
||||
protected string $script = 'scrape-messages.spec.js';
|
||||
protected int $processTimeout = 600;
|
||||
|
||||
protected function afterRun(): void
|
||||
{
|
||||
$path = base_path('playwright/output/scrape-messages.json');
|
||||
|
||||
if (! file_exists($path)) {
|
||||
$this->warn('No output file found — nothing to upsert.');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents($path), true);
|
||||
|
||||
foreach ($data['threads'] ?? [] as $thread) {
|
||||
DirectThread::updateOrCreate(
|
||||
['thread_id' => $thread['thread_id']],
|
||||
[
|
||||
'thread_key' => $thread['thread_key'],
|
||||
'thread_title' => $thread['thread_title'],
|
||||
'is_group' => $thread['is_group'],
|
||||
'participants' => $thread['participants'],
|
||||
'last_activity_at' => $thread['last_activity_at'],
|
||||
'muted' => $thread['muted'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($data['messages'] ?? [] as $message) {
|
||||
DirectMessage::updateOrCreate(
|
||||
['id' => $message['id']],
|
||||
[
|
||||
'thread_id' => $message['thread_id'],
|
||||
'user_id' => $message['user_id'],
|
||||
'username' => $message['username'],
|
||||
'full_name' => $message['full_name'],
|
||||
'item_type' => $message['content_type'],
|
||||
'text' => $message['text'],
|
||||
'timestamp' => $message['timestamp_ms'],
|
||||
'shared_url' => $message['share']['url'] ?? null,
|
||||
'shared_preview_image' => $message['share']['preview_image'] ?? null,
|
||||
'shared_title' => $message['share']['title'] ?? null,
|
||||
'shared_caption' => $message['share']['caption'] ?? null,
|
||||
'reactions' => $message['reactions'],
|
||||
'replied_to_id' => $message['replied_to_id'],
|
||||
'replied_to_text' => $message['replied_to_text'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->info(sprintf(
|
||||
'Upserted %d threads and %d messages.',
|
||||
count($data['threads'] ?? []),
|
||||
count($data['messages'] ?? [])
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Post;
|
||||
use Illuminate\Console\Attributes\Description;
|
||||
use Illuminate\Console\Attributes\Signature;
|
||||
|
||||
#[Signature('direct:scrape-feed')]
|
||||
#[Description('Scrape the Instagram following feed via Playwright')]
|
||||
class ScrapeFeed extends RunsPlaywrightScript
|
||||
{
|
||||
protected string $script = 'scrape-feed.spec.js';
|
||||
|
||||
protected function afterRun(): void
|
||||
{
|
||||
$path = base_path('playwright/output/scrape-feed.json');
|
||||
|
||||
if (! file_exists($path)) {
|
||||
$this->warn('No output file found — nothing to upsert.');
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = json_decode(file_get_contents($path), true);
|
||||
|
||||
foreach ($posts as $post) {
|
||||
Post::updateOrCreate(
|
||||
['id' => $post['id']],
|
||||
[
|
||||
'code' => $post['code'],
|
||||
'username' => $post['username'],
|
||||
'full_name' => $post['full_name'],
|
||||
'caption' => $post['caption'],
|
||||
'accessibility_caption' => $post['accessibility_caption'],
|
||||
'is_video' => $post['is_video'],
|
||||
'is_carousel' => $post['is_carousel'],
|
||||
'like_count' => $post['like_count'],
|
||||
'comment_count' => $post['comment_count'],
|
||||
'taken_at' => $post['taken_at'],
|
||||
'image_url' => $post['image_url'],
|
||||
'video_url' => $post['video_url'],
|
||||
'carousel_images' => $post['carousel_images'],
|
||||
'profile_pic_url' => $post['profile_pic_url'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->info('Upserted ' . count($posts) . ' posts.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Story;
|
||||
use Illuminate\Console\Attributes\Description;
|
||||
use Illuminate\Console\Attributes\Signature;
|
||||
|
||||
#[Signature('direct:scrape-stories')]
|
||||
#[Description('Scrape Instagram story content via Playwright')]
|
||||
class ScrapeStories extends RunsPlaywrightScript
|
||||
{
|
||||
protected string $script = 'scrape-story-content.spec.js';
|
||||
|
||||
protected function afterRun(): void
|
||||
{
|
||||
$path = base_path('playwright/output/scrape-stories.json');
|
||||
|
||||
if (! file_exists($path)) {
|
||||
$this->warn('No output file found — nothing to upsert.');
|
||||
return;
|
||||
}
|
||||
|
||||
$items = json_decode(file_get_contents($path), true);
|
||||
|
||||
foreach ($items as $item) {
|
||||
Story::updateOrCreate(
|
||||
['id' => $item['id']],
|
||||
[
|
||||
'user_id' => $item['user_id'],
|
||||
'username' => $item['username'],
|
||||
'profile_pic_url' => $item['profile_pic_url'],
|
||||
'code' => $item['code'],
|
||||
'is_video' => $item['is_video'],
|
||||
'taken_at' => $item['taken_at'],
|
||||
'expiring_at' => $item['expiring_at'],
|
||||
'image_url' => $item['image_url'],
|
||||
'video_url' => $item['video_url'],
|
||||
'accessibility_caption' => $item['accessibility_caption'],
|
||||
'muted' => $item['muted'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->info('Upserted ' . count($items) . ' story items.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user