Instagram repost job
Some checks failed
Push image to registry / build-image (push) Failing after 6m44s

This commit is contained in:
2025-06-03 19:10:34 +02:00
parent 25a9063169
commit 7f50822692
12 changed files with 907 additions and 4 deletions

View File

@ -54,6 +54,7 @@ RUN apk update && apk add --no-cache \
openssl \
linux-headers \
supervisor \
yt-dlp \
&& rm -rf /tmp/* /var/cache/apk/*
RUN docker-php-ext-configure zip && docker-php-ext-install zip

View File

@ -0,0 +1,54 @@
<?php
namespace App\Browser\Jobs\InstagramRepost;
use DateTimeImmutable;
/**
* Interface for Instagram video handling.
* Holds the data of a video from Instagram.
*/
interface IInstagramVideo
{
/**
* Get the URL of the video.
*
* @return string The URL of the video.
*/
public function getUrl(): string;
/**
* Get the title of the video.
*
* @return string The title of the video.
*/
public function getTitle(): string;
/**
* Get the caption of the video.
*
* @return string The caption of the video.
*/
public function getDescription(): string;
/**
* Get the date when the video was posted.
*
* @return ?DateTimeImmutable The date of the post.
*/
public function getPostDate(): ?DateTimeImmutable;
/**
* Get the filename of the video.
*
* @return string The filename of the video.
*/
public function getFilename(): string;
/**
* Set the filename of the video.
*
* @param string $filename The filename to set.
*/
public function setFilename(string $filename): void;
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Browser\Jobs\InstagramRepost;
use App\Models\JobRun;
use Illuminate\Support\Collection;
interface IInstagramVideoDownloader
{
/**
* Download the video from the given Instagram post URL.
*
* @param int $jobId The ID of the job for which the video is being downloaded.
* @param JobRun $jobRun The job run instance for logging and tracking.
* @param string $postUrl The URL of the Instagram post.
* @param Collection $jobInfos The job information collection.
* @param string $downloadFolder The folder where the video should be downloaded.
* @param string $accountEmail The email or username of the Instagram account.
* @param string $accountPassword The password for the Instagram account.
* @return IInstagramVideo|null The path to the downloaded video file, or null if the download failed.
*/
public function downloadVideo(int $jobId, JobRun $jobRun, string $postUrl, string $downloadFolder, string $accountEmail, string $accountPassword): ?IInstagramVideo;
}

View File

@ -0,0 +1,406 @@
<?php
namespace App\Browser\Jobs\InstagramRepost;
use App\Browser\BrowserJob;
use App\Browser\JobDebugScreenshot;
use App\Models\InstagramRepost;
use App\Models\InstagramAccount;
use App\Models\Job;
use App\Models\JobRun;
use App\Notification\Notifications\JobDebugNotification;
use App\Notification\Providers\AllNotification;
use Facebook\WebDriver\WebDriverBy;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Laravel\Dusk\Browser;
class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
{
// === CONFIGURATION ===
// TODO : put that in laravel config file
/**
* Maximum number of posts to repost per account
* @var int
*/
private const MAX_REPOSTS_PER_ACCOUNT = 2;
/**
* Max number of reposts per job
* This is the maximum number of posts that will be reposted in a single job run.
* @var int
*/
private const MAX_REPOSTS_PER_JOB = 3;
/**
* Maximum number of tries to repost a reel
* If a reel fails to be reposted, it will be retried up to this number of times.
* @var int
*/
private const MAX_REPOST_TRIES = 3;
private const APPROXIMATIVE_RUNNING_MINUTES = 2;
private Collection $jobInfos;
protected JobRun $jobRun;
protected IInstagramVideoDownloader $videoDownloader;
protected string $downloadFolder = "app/Browser/downloads/InstagramRepost/";
public function __construct($jobId = 4)
{
parent::__construct($jobId);
$this->downloadFolder = base_path($this->downloadFolder);
$this->videoDownloader = new YTDLPDownloader();
}
public function run(Browser $browser): ?JobRun
{
Log::info("Running InstagramRepostJob");
$this->jobInfos = Job::find($this->jobId)->jobInfosTable();
$this->jobRun = new JobRun([
"job_id" => $this->jobId,
"success" => false,
]);
$this->jobRun->save();
$browser->visit('https://instagram.com');
sleep(5);
$this->removePopups($browser);
sleep(2);
$this->signin($browser);
sleep(2);
$this->repostLatestPosts($browser);
sleep(5);
$this->jobRun->success = true;
$this->jobRun->save();
Log::info("InstagramRepostJob run ended");
return $this->jobRun;
}
/**
* @inheritDoc
*/
public function runTest(Browser $browser): ?JobRun
{
$this->jobInfos = Job::find($this->jobId)->jobInfosTable();
try {
$browser->visit('https://instagram.com');
sleep(2);
$this->removePopups($browser);
sleep(2);
$this->signin($browser);
sleep(3);
return $this->makeSimpleJobRun(
true,
"Connexion réussie",
"Datboi a réussi à se connecter sur Instagram"
);
} catch (\Exception $e) {
return $this->makeSimpleJobRun(
false,
"Connexion échouée",
"Datboi n'a pas réussi à se connecter sur Instagram :\n" . $e->getMessage()
);
}
}
protected function repostLatestPosts(Browser $browser) {
try {
// Download the latest reels from the accounts specified in the job infos
$toDownloadReels = []; // Array to store to download reels to post later
$accounts = explode(",", $this->jobInfos->get("instagram_repost_accounts"));
foreach ($accounts as $account) {
$account = trim($account);
$toDownloadReels = array_merge($toDownloadReels, $this->getLatestReelsFromAccount($browser, $account));
}
// Add unreposted reels to the job run if not enough reels were downloaded
if (count($toDownloadReels) < self::MAX_REPOSTS_PER_JOB) {
$unrepostedReels = InstagramRepost::where("reposted", false)
->where("repost_tries", "<", self::MAX_REPOST_TRIES) // Limit to 3 tries
->whereIn("account_id", InstagramAccount::whereIn("username", $accounts)->pluck("id"))
->take(self::MAX_REPOSTS_PER_JOB - count($toDownloadReels))
->get();
foreach ($unrepostedReels as $reel) {
$toDownloadReels[] = $reel;
}
}
// Shuffling and keeping only the x first posts
shuffle($toDownloadReels);
$toDownloadReels = array_slice($toDownloadReels, 0, self::MAX_REPOSTS_PER_JOB);
// Download the reels
$downloadedReels = [];
foreach ($toDownloadReels as $repost) {
$downloadedReels[] = [
$repost,
$this->downloadReel(
$browser,
$repost
)
];
}
// Now repost all downloaded reels
foreach ($downloadedReels as $infos) {
$reel = $infos[0];
$videoInfo = $infos[1];
try {
// TODO : Avoid getting the reel from the db again, store it in the downloadedReels array
$this->repostReel($browser, InstagramRepost::where('reel_id', $reel->reel_id)->first(), $videoInfo);
} catch (\Exception $e) {
Log::error("Failed to repost reel: {$videoInfo->getTitle()} - " . $e->getMessage());
AllNotification::send(new JobDebugNotification($this->jobId, "Failed to repost reel: {$videoInfo->getTitle()} - " . $e->getMessage()));
}
}
} catch (\Exception $e) {
dump($e->getMessage());
} finally {
// Removes all videos in the download folder
$files = glob($this->downloadFolder . '*'); // Get all files in the download folder
foreach ($files as $file) {
if (is_file($file)) {
unlink($file); // Delete the file
}
}
}
}
private function getLatestReelsFromAccount(Browser $browser, string $account): array
{
$accountReels = []; // Indexed array to store new reels from the account
$browser->visit("https://instagram.com/{$account}/reels");
sleep(3);
$browser->waitForText("followers", 10, true);
// If we are here, the account exists
$accountModel = InstagramAccount::where("username", $account)->first();
if ($accountModel == null) { // Does not exist in the database yet
$accountModel = new InstagramAccount([
"username" => $account,
]);
$accountModel->save();
Log::info("New Instagram account added: {$account}");
} else {
Log::debug("Instagram account already exists: {$account}");
}
$repostedPosts = $accountModel->reposts()->where("reposted", true)->pluck("reel_id");
// Posts must be sorted by latest post date first
// TODO : Scroll when not enough post are shown
$posts = $browser->driver->findElements(WebDriverBy::xpath('//a[contains(@href, "'.$account.'/reel/")][not(.//*[local-name() = "svg"][@aria-label="Pinned post icon"])]'));
foreach ($posts as $post) {
$postUrl = $post->getAttribute('href');
$postId = explode("/", $postUrl)[3] ?? null;
if ($postId === null) {
AllNotification::send(new JobDebugNotification($this->jobId, "Can't get Instagram post ID from url"));
continue;
}
// Break the loop if the post has already been reposted
if ($repostedPosts->contains($postId)) {
Log::debug("Post already reposted: {$postUrl}");
break;
}
$reelModel = InstagramRepost::firstOrCreate(
["reel_id" => $postId, "account_id" => $accountModel->id],
["reposted" => false, "repost_tries" => 0]
);
if (count($accountReels) < self::MAX_REPOSTS_PER_ACCOUNT) {
$accountReels[] = $reelModel; // Add it to the to be downloaded reels array
}
}
return $accountReels;
}
private function downloadReel(Browser $browser, InstagramRepost $reel): ?IInstagramVideo
{
$videoInfo = $this->videoDownloader->downloadVideo(
$this->jobId,
$this->jobRun,
$reel->getUrl(),
$this->downloadFolder,
$this->jobInfos->get("instagram_repost_account_email"),
$this->jobInfos->get("instagram_repost_account_password")
);
if ($videoInfo === null) {
Log::error("Failed to download video for post: {$reel->reel_id}");
return null;
}
else {
// Set the filename to the post ID
$newFilename = $this->downloadFolder . $reel->reel_id . ".mp4";
rename($videoInfo->getFilename(), $newFilename);
$videoInfo->setFilename($newFilename);
}
Log::info("Downloaded video: {$videoInfo->getTitle()} : {$videoInfo->getDescription()}");
return $videoInfo;
}
protected function repostReel(Browser $browser, InstagramRepost $reel, IInstagramVideo $videoInfo)
{
Log::info("Reposting reel: {$reel->reel_id} - {$videoInfo->getTitle()}");
// Increment the repost tries
$reel->repost_tries++;
$reel->save();
// TODO Reset if a problem occurs and try again with a limit of 3 attempts
$browser->visit('https://instagram.com');
sleep(2);
// Navigate to the reel upload page
$createButton = $browser->driver->findElement(WebDriverBy::xpath('//a[./div//span[contains(text(), "Create")]]'));
$createButton->click();
sleep(2);
$newPostButton = $browser->driver->findElement(WebDriverBy::xpath('//a[./div//span[contains(text(), "Post")]][@href="#"]'));
$newPostButton->click();
sleep(3);
// Upload the video file
$selectFileButton = $browser->driver->findElement(WebDriverBy::xpath('//button[contains(text(), "Select from computer")]'));
$selectFileButton->click();
sleep(2);
$browser->attach('input[type="file"]._ac69', $this->downloadFolder . $reel->reel_id . ".mp4");
sleep(5); // TODO : Wait for the file to be uploaded
$this->removePopups($browser);
sleep(2);
// Put original resolution
$this->putOriginalResolution($browser);
$this->clickNext($browser);
$this->clickNext($browser); // Skip cover photo and trim
// Add a caption
$captionInput = $browser->driver->findElement(WebDriverBy::xpath('//div[@contenteditable]'));
$captionInput->sendKeys($videoInfo->getDescription());
sleep(2); // Wait for the caption to be added
$this->clickNext($browser); // Share the post
sleep(5); // Wait for the post to be completed
// Check if the post was successful
try {
$browser->waitForText("Your reel has been shared.", 60, true);
Log::info("Reel reposted successfully: {$reel->reel_id}");
// Mark the reel as reposted in the database
$reel->reposted = true;
$reel->save();
} catch (\Exception $e) {
try {
$browser->waitForText("Your post was shared", 60, true);
$closeButton = $browser->driver->findElement(WebDriverBy::xpath('//div[./div/*[local-name() = "svg"][@aria-label="Close"]]'));
$closeButton->click();
} catch (\Exception $e) {
// Do nothing
}
Log::error("Failed to repost reel: {$reel->reel_id} - " . $e->getMessage());
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "Failed to repost reel: {$reel->reel_id} - " . $e->getMessage()));
}
}
private function clickNext(Browser $browser) {
$nextButton = $browser->driver->findElement(WebDriverBy::xpath('//div[contains(text(), "Next") or contains(text(), "Share")]'));
$nextButton->click();
sleep(2);
}
private function putOriginalResolution(Browser $browser)
{
try {
$chooseResolutionButton = $browser->driver->findElement(WebDriverBy::xpath('//button[./div/*[local-name() = "svg"][@aria-label="Select crop"]]'));
$chooseResolutionButton->click();
sleep(2);
// Choos "original" resolution
$originalResolutionButton = $browser->driver->findElement(WebDriverBy::xpath('//div[./div/div/span[contains(text(), "Original")]]'));
$originalResolutionButton->click();
sleep(2);
} catch (\Exception $e) {
Log::error("Failed to set original resolution: " . $e->getMessage());
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "Failed to set \"original\" resolution: " . $e->getMessage()));
}
}
protected function signin(Browser $browser)
{
try {
$browser->waitForText("Log in", 10, true);
sleep(3);
$emailButton = $browser->driver->findElement(WebDriverBy::xpath('//input[contains(@aria-label, "email")]'));
$emailButton->click();
$emailButton->sendKeys($this->jobInfos->get("instagram_repost_account_email"));
sleep(3);
$passwordButton = $browser->driver->findElement(WebDriverBy::xpath('//input[contains(@aria-label, "Password")]'));
$passwordButton->click();
$passwordButton->sendKeys($this->jobInfos->get("instagram_repost_account_password") . "\n");
} catch (\Exception $e) {
// Probably no need to signin
}
}
protected function removePopups(Browser $browser)
{
try {
$cookiesAllowButton = $browser->driver->findElement(WebDriverBy::xpath('//button[contains(text(), "Allow all cookies")]'));
$cookiesAllowButton->click();
sleep(2);
} catch (\Exception $e) {
// No cookie popup found, continue
}
try {
$notNowButton = $browser->driver->findElement(WebDriverBy::xpath('//button[contains(text(), "Not Now")]'));
$notNowButton->click();
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "Popup Not Now clicked"));
sleep(2);
} catch (\Exception $e) {
// No "Not Now" popup found, continue
}
try {
$okButton = $browser->driver->findElement(WebDriverBy::xpath('//button[contains(text(), "OK")]'));
$okButton->click();
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
AllNotification::send(new JobDebugNotification($this->jobId, "Popup Ok clicked"));
sleep(2);
} catch (\Exception $e) {
// No "Ok" popup found, continue
}
// $browser->script('document.querySelector("div.app-modal")[0].remove();');
// $browser->driver->executeScript('document.querySelector("div.app-modal")[0].remove();');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Browser\Jobs\InstagramRepost;
use App\Models\JobArtifact;
use App\Models\JobRun;
use App\Notification\Notifications\JobDebugNotification;
use App\Notification\Providers\AllNotification;
use Illuminate\Support\Facades\Log;
use YoutubeDl\Options;
use YoutubeDl\YoutubeDl;
class YTDLPDownloader implements IInstagramVideoDownloader
{
/**
* @inheritDoc
*/
public function downloadVideo(int $jobId, JobRun $jobRun, string $postUrl, string $downloadFolder, string $accountEmail, string $accountPassword): ?YTDLPVideo
{
$dl = new YoutubeDl();
$options = Options::create()
->downloadPath($downloadFolder)
->apLogin($accountEmail, $accountPassword)
->checkAllFormats(true)
->format('bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]')
->url($postUrl);
try {
$videosCollection = $dl->download($options);
foreach ($videosCollection->getVideos() as $video) {
if ($video->getError() !== null) {
$jobRun->addArtifact(new JobArtifact([
"name" => "Erreur lors du téléchargement de la vidéo \"{$video->getTitle()}\"",
"content" => $video->getError(),
]));
Log::error("Error downloading video: " . $video->getError());
return null; // Return null if there was an error downloading the video
} else {
$IVideo = new YTDLPVideo(
$video->getWebpageUrl(),
$video->getTitle(),
$video->getDescription() ?? "",
$video->getUploadDate(),
$video->getFilename(),
);
return $IVideo; // Return the video object if download was successful
}
}
} catch (\Exception $e) {
AllNotification::send(new JobDebugNotification($jobId, "Error while downloading video: " . $e->getMessage()));
return null;
}
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Browser\Jobs\InstagramRepost;
use DateTimeImmutable;
class YTDLPVideo implements IInstagramVideo
{
private string $url;
private string $title;
private string $description;
private ?DateTimeImmutable $postDate;
private string $fileName;
public function __construct(string $url, string $title, string $description, ?DateTimeImmutable $postDate, string $filename)
{
$this->url = $url;
$this->title = $title;
$this->description = $description;
$this->postDate = $postDate;
$this->fileName = $filename;
}
/**
* @inheritDoc
*/
public function getUrl(): string
{
return $this->url;
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @inheritDoc
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @inheritDoc
*/
public function getPostDate(): ?DateTimeImmutable
{
return $this->postDate;
}
/**
* @inheritDoc
*/
public function getFilename(): string
{
return $this->fileName;
}
/**
* @inheritDoc
*/
public function setFilename(string $filename): void
{
$this->fileName = $filename;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InstagramAccount extends Model
{
protected $table = 'instagram_repost_accounts';
protected $fillable = [
'username',
];
public function reposts()
{
return $this->hasMany(InstagramRepost::class, 'account_id');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InstagramRepost extends Model
{
protected $table = 'instagram_reposts';
protected $fillable = [
'reel_id',
'reposted',
'account_id',
];
public function account()
{
return $this->belongsTo(InstagramAccount::class, 'account_id');
}
public function getUrl(): string
{
return "https://www.instagram.com/reel/{$this->reel_id}/";
}
}

View File

@ -9,7 +9,7 @@
],
"license": "MIT",
"require": {
"php": "^8.2",
"php": "^8.3",
"erusev/parsedown": "^1.7",
"inertiajs/inertia-laravel": "^2.0",
"laravel/dusk": "^8.2",
@ -18,6 +18,7 @@
"laravel/sanctum": "^4.0",
"laravel/telescope": "^5.5",
"laravel/tinker": "^2.9",
"norkunas/youtube-dl-php": "dev-master",
"tightenco/ziggy": "^2.0"
},
"require-dev": {

137
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6008577001548e6e63c074be98000d97",
"content-hash": "9a964008040d9ce219547515fe65dd86",
"packages": [
{
"name": "brick/math",
@ -3011,6 +3011,69 @@
},
"time": "2024-12-30T11:07:19+00:00"
},
{
"name": "norkunas/youtube-dl-php",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/norkunas/youtube-dl-php.git",
"reference": "4c954b3b8c6b30d0c0135ec758b61a91f0ac3b6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/norkunas/youtube-dl-php/zipball/4c954b3b8c6b30d0c0135ec758b61a91f0ac3b6a",
"reference": "4c954b3b8c6b30d0c0135ec758b61a91f0ac3b6a",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=7.4.0",
"symfony/filesystem": "^5.1|^6.0|^7.0",
"symfony/polyfill-php80": "^1.28",
"symfony/process": "^5.1|^6.0|^7.0"
},
"require-dev": {
"mikey179/vfsstream": "^1.6.11",
"php-cs-fixer/shim": "^3.60",
"phpstan/phpstan": "^1.11.8",
"phpstan/phpstan-phpunit": "^1.4.0",
"phpstan/phpstan-strict-rules": "^1.6.0",
"symfony/phpunit-bridge": "^6.4.10"
},
"default-branch": true,
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"YoutubeDl\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Tomas Norkūnas",
"email": "norkunas.tom@gmail.com"
}
],
"description": "youtube-dl / yt-dlp wrapper for php",
"keywords": [
"youtube",
"youtube-dl",
"yt-dlp"
],
"support": {
"issues": "https://github.com/norkunas/youtube-dl-php/issues",
"source": "https://github.com/norkunas/youtube-dl-php/tree/v2.10.0"
},
"time": "2025-02-20T17:32:37+00:00"
},
{
"name": "nunomaduro/termwind",
"version": "v2.3.0",
@ -5211,6 +5274,72 @@
],
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/filesystem",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"require-dev": {
"symfony/process": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v7.3.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-10-25T15:15:23+00:00"
},
{
"name": "symfony/finder",
"version": "v7.2.2",
@ -9591,11 +9720,13 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"norkunas/youtube-dl-php": 20
},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": "^8.2"
"php": "^8.3"
},
"platform-dev": [],
"plugin-api-version": "2.6.0"

View File

@ -0,0 +1,114 @@
<?php
use App\Models\InstagramAccount;
use App\Models\InstagramRepost;
use App\Models\Job;
use App\Models\JobInfo;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$newJobId = 4;
Job::forceCreate([
"id" => $newJobId,
"name" => "Instagram Repost",
"description" => "Reposte les publications Instagram des comptes donnés.",
]);
JobInfo::forceCreate([
"key" => "instagram_repost_accounts",
"name" => "Comptes Instagram à reposter",
"description" => "Liste des noms des comptes Instagram à partir desquels les publications seront repostées.\nSéparez les comptes par des virgules.",
"placeholder" => "is.it.ninluc, freddiedredd",
"is_required" => true,
"job_info_type_id" => 1,
"job_id" => $newJobId,
]);
JobInfo::forceCreate([
"key" => "instagram_repost_account_email",
"name" => "Identifiant",
"description" => "L'adresse e-mail/nom d'utilisateur/N° de téléphone utilisée pour le compte Instagram de repost.",
"is_required" => true,
"job_info_type_id" => 1,
"job_id" => $newJobId,
]);
JobInfo::forceCreate([
"key" => "instagram_repost_account_password",
"name" => "Mot de passe",
"description" => "Le mot de passe utilisée pour le compte Instagram de repost.",
"is_required" => true,
"job_info_type_id" => 3,
"job_id" => $newJobId,
]);
Schema::create('instagram_repost_accounts', function (Blueprint $table) {
$table->id();
$table->string("username")->unique();
$table->timestamps();
});
Schema::create('instagram_reposts', function (Blueprint $table) {
$table->id();
$table->string("reel_id")->unique();
$table->boolean("reposted")->default(false);
$table->integer("repost_tries")->default(0);
$table->foreignIdFor(InstagramAccount::class, "account_id")
->constrained('instagram_repost_accounts')
->cascadeOnDelete();
$table->timestamps();
});
// Already reposted posts
$notDeadLmaoAccount = InstagramAccount::forceCreate([
"username" => "notdeadlmao69",
]);
$negusflexAccount = InstagramAccount::forceCreate([
"username" => "negusflex",
]);
InstagramRepost::forceCreate([
"reel_id" => "DKbW7M_RWV7",
"reposted" => true,
"account_id" => $notDeadLmaoAccount->id,
]);
InstagramRepost::forceCreate([
"reel_id" => "DKccuTMTmP_",
"reposted" => true,
"account_id" => $negusflexAccount->id,
]);
InstagramRepost::forceCreate([
"reel_id" => "DJmUjhWSnqm",
"reposted" => true,
"account_id" => $negusflexAccount->id,
]);
InstagramRepost::forceCreate([
"reel_id" => "DKcdSGnv6uq",
"reposted" => true,
"account_id" => $negusflexAccount->id,
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Job::where("id", 4)->delete();
JobInfo::where("job_id", 4)->delete();
Schema::dropIfExists('instagram_repost_accounts');
Schema::dropIfExists('instagram_reposts');
}
};

View File

@ -2,6 +2,7 @@
use App\Browser\Jobs\Hellcase\HellcaseJob;
use App\Browser\Jobs\HellcaseBattles\HellcaseBattlesJob;
use App\Browser\Jobs\InstagramRepost\InstagramRepostJob;
use App\Jobs\PruneOldJobRuns;
use App\Services\BrowserJobsInstances;
use Illuminate\Foundation\Inspiring;
@ -23,3 +24,4 @@ Schedule::job(new PruneOldJobRuns)->monthly()->onOneServer()->withoutOverlapping
Schedule::job(new HellcaseJob)->daily()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
// Schedule::job(new HellcaseJob)->everyMinute()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
Schedule::job(new HellcaseBattlesJob)->hourly()->onOneServer()->withoutOverlapping()->name('hellcase_battles')->description('Hellcase battles job');
Schedule::job(new InstagramRepostJob())->everyThreeHours()->onOneServer()->withoutOverlapping()->name('instagram_reposts')->description('Intagrame reposts job');