teknolis / app /Console /Commands /PublishScheduledSocialPosts.php
Codex
Deploy latest assistant, DeepSeek defaults, and automation updates
74acf19
Raw
History Blame Contribute Delete
5.62 kB
<?php
namespace App\Console\Commands;
use App\Models\SocialAutomationSetting;
use App\Models\SocialPost;
use App\Services\SocialPostMediaPreparationService;
use App\Services\SocialPublisherService;
use Illuminate\Console\Command;
class PublishScheduledSocialPosts extends Command
{
protected $signature = 'social:publish-scheduled {--limit=20 : Jumlah post yang diproses per run}';
protected $description = 'Publish social posts yang status scheduled dan waktunya sudah lewat.';
public function handle(
SocialPublisherService $publisher,
SocialPostMediaPreparationService $mediaPreparation
): int
{
$limit = max(1, (int) $this->option('limit'));
$posts = SocialPost::query()
->where('status', 'scheduled')
->where('requires_approval', false)
->whereNotNull('scheduled_at')
->where('scheduled_at', '<=', now())
->where(function ($q) {
$q->whereNull('next_retry_at')
->orWhere('next_retry_at', '<=', now());
})
->with('user.socialPlatformCredentials')
->orderBy('scheduled_at')
->limit($limit)
->get();
if ($posts->isEmpty()) {
$this->info('Tidak ada scheduled post yang siap publish.');
return self::SUCCESS;
}
foreach ($posts as $post) {
$setting = $this->settingFor($post->user_id);
[$allowed, $limitMessage] = $this->canPublishNow($post->user_id, $post->platform, $setting);
if (! $allowed) {
$this->warn("[{$post->id}] skip: {$limitMessage}");
continue;
}
$credential = $post->user->socialPlatformCredentials
->firstWhere('platform', $post->platform);
if (! $credential) {
$this->markFailure($post, 'Credential platform tidak ditemukan.', $setting);
$this->warn("[{$post->id}] gagal: credential {$post->platform} tidak ada.");
continue;
}
try {
if ($mediaPreparation->prepareForPublishing($post)) {
$post->refresh();
}
$result = $publisher->publish($post, $credential);
$currentMeta = is_array($post->meta) ? $post->meta : [];
$currentMeta['publish_payload'] = $result['payload'] ?? [];
$post->update([
'status' => 'posted',
'posted_at' => now(),
'approved_at' => $post->approved_at ?? now(),
'external_post_id' => $result['external_post_id'] ?? null,
'error_message' => null,
'meta' => $currentMeta,
'next_retry_at' => null,
]);
$this->info("[{$post->id}] posted ke {$post->platform}.");
} catch (\Throwable $e) {
$this->markFailure($post, $e->getMessage(), $setting);
$this->error("[{$post->id}] gagal: {$e->getMessage()}");
}
}
return self::SUCCESS;
}
private function settingFor(int $userId): SocialAutomationSetting
{
return SocialAutomationSetting::query()->firstOrCreate(
['user_id' => $userId],
[
'approval_mode' => 'manual',
'daily_limit' => 20,
'platform_daily_limits' => [
'facebook' => 8,
'instagram' => 8,
'tiktok' => 4,
'x' => 12,
],
'max_retries' => 2,
'retry_delay_minutes' => 15,
]
);
}
private function canPublishNow(int $userId, string $platform, SocialAutomationSetting $setting): array
{
$today = now()->toDateString();
$postedToday = SocialPost::query()
->where('user_id', $userId)
->where('status', 'posted')
->whereDate('posted_at', $today)
->count();
if ($postedToday >= $setting->daily_limit) {
return [false, 'batas harian total tercapai'];
}
$platformLimits = $setting->platform_daily_limits ?? [];
$platformLimit = (int) ($platformLimits[$platform] ?? 0);
if ($platformLimit > 0) {
$postedPerPlatform = SocialPost::query()
->where('user_id', $userId)
->where('platform', $platform)
->where('status', 'posted')
->whereDate('posted_at', $today)
->count();
if ($postedPerPlatform >= $platformLimit) {
return [false, "batas harian {$platform} tercapai"];
}
}
return [true, null];
}
private function markFailure(SocialPost $post, string $message, SocialAutomationSetting $setting): void
{
$attempt = $post->attempt_count + 1;
$maxRetries = max(0, (int) $setting->max_retries);
if ($attempt <= $maxRetries) {
$post->update([
'status' => 'scheduled',
'attempt_count' => $attempt,
'next_retry_at' => now()->addMinutes(max(1, (int) $setting->retry_delay_minutes)),
'error_message' => $message,
]);
return;
}
$post->update([
'status' => 'failed',
'attempt_count' => $attempt,
'error_message' => $message,
'next_retry_at' => null,
]);
}
}