Files
animexe/app/Console/Commands/GenerateBlogPosts.php
T
2026-07-14 00:01:48 +03:00

136 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Console\Commands;
use App\Models\Anime;
use App\Models\BlogPost;
use App\Services\DeepSeekService;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class GenerateBlogPosts extends Command
{
protected $signature = 'animexe:generate-blogs
{--count=2 : Kaç blog yazısı üretileceği}
{--anime= : Belirli bir anime ID için üret}
{--force : Daha önce blog yazılmış animeler için de üret}';
protected $description = 'DeepSeek AI ile anime blog yazıları üretir';
public function handle(DeepSeekService $deepseek): int
{
if (!$deepseek->isConfigured()) {
$this->error('DeepSeek API anahtarı ayarlanmamış. Admin > Ayarlar > deepseek_api_key');
return self::FAILURE;
}
$count = (int) $this->option('count');
$animeId = $this->option('anime');
$force = $this->option('force');
if ($animeId) {
$animes = Anime::where('id', $animeId)->where('is_published', true)->with('genres')->get();
} else {
$alreadyBlogged = $force ? [] : BlogPost::whereNotNull('anime_id')->pluck('anime_id')->toArray();
$animesQuery = Anime::where('is_published', true)
->whereNotIn('id', $alreadyBlogged)
->with('genres')
->orderByDesc('rating')
->limit($count * 3)
->get();
$randomResult = $animesQuery->random(min($count, $animesQuery->count()));
$animes = collect($randomResult);
}
if ($animes->isEmpty()) {
$this->info('Blog yazısı üretilecek anime bulunamadı.');
return self::SUCCESS;
}
$generated = 0;
foreach ($animes->take($count) as $anime) {
$this->info("Blog üretiliyor: {$anime->title}...");
// Aynı türden ilgili animeler bul
$genreIds = $anime->genres->pluck('id');
$related = Anime::where('is_published', true)
->where('id', '!=', $anime->id)
->whereHas('genres', fn($q) => $q->whereIn('genres.id', $genreIds))
->orderByDesc('rating')
->limit(5)
->get(['id', 'title', 'slug'])
->map(fn($a) => ['slug' => $a->slug, 'title' => $a->title])
->toArray();
$data = $deepseek->generateBlogPost($anime, $related);
if (!$data || empty($data['content'])) {
$this->warn(" [{$anime->title}] için içerik üretilemedi: " . $deepseek->lastError);
continue;
}
// [LINK:slug]Title[/LINK] placeholder'larını gerçek URL'lerle değiştir
$content = preg_replace_callback(
'/\[LINK:([^\]]+)\]([^\[]*)\[\/LINK\]/',
function ($m) {
$slug = trim($m[1]);
$label = trim($m[2]);
try {
$url = route('anime.show', $slug);
return "<a href=\"{$url}\">{$label}</a>";
} catch (\Exception $e) {
return $label;
}
},
$data['content'] ?? ''
);
$title = $data['title'] ?? ($anime->title . ' İzle — Animexe Rehberi');
$slug = BlogPost::generateSlug($title);
// Linked anime IDs
$linkedIds = [];
if (!empty($data['linked_slugs'])) {
$linkedIds = Anime::whereIn('slug', $data['linked_slugs'])->pluck('id')->toArray();
}
$readingTime = max(3, (int) (str_word_count(strip_tags($content)) / 200));
BlogPost::create([
'title' => $title,
'slug' => $slug,
'excerpt' => $data['excerpt'] ?? '',
'content' => $content,
'cover_image' => $anime->cover_image,
'focus_keyword' => $data['focus_keyword'] ?? $anime->title,
'meta_title' => $data['title'] ?? null,
'meta_description' => $data['meta_description'] ?? $data['excerpt'] ?? '',
'meta_keywords' => implode(', ', array_filter([
$anime->title,
$anime->title . ' izle',
'türkçe anime',
$data['focus_keyword'] ?? '',
])),
'status' => 'published',
'ai_generated' => true,
'anime_id' => $anime->id,
'linked_anime_ids' => $linkedIds,
'faq' => $data['faq'] ?? [],
'reading_time' => $readingTime,
'published_at' => now(),
]);
$this->info(" ✓ Blog yazısı oluşturuldu: {$title}");
$generated++;
// API rate limit
sleep(2);
}
$this->info("Tamamlandı. {$generated} blog yazısı üretildi.");
return self::SUCCESS;
}
}