Implement creator studio and upload updates
This commit is contained in:
168
app/Console/Commands/RescanContentModerationCommand.php
Normal file
168
app/Console/Commands/RescanContentModerationCommand.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ModerationContentType;
|
||||
use App\Enums\ModerationStatus;
|
||||
use App\Models\ContentModerationFinding;
|
||||
use App\Services\Moderation\ContentModerationActionLogService;
|
||||
use App\Services\Moderation\ContentModerationProcessingService;
|
||||
use App\Services\Moderation\ContentModerationSourceService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RescanContentModerationCommand extends Command
|
||||
{
|
||||
protected $signature = 'skinbase:rescan-content-moderation
|
||||
{--only= : comments, descriptions, titles, bios, profile-links, collections, stories, cards, or a comma-separated list}
|
||||
{--status=pending : Filter findings by moderation status}
|
||||
{--limit= : Maximum number of findings to rescan}
|
||||
{--from-id= : Start rescanning at or after this finding ID}
|
||||
{--force : Rescan all matching findings, including already resolved findings}';
|
||||
|
||||
protected $description = 'Rescan existing moderation findings using the latest rules and scanner version.';
|
||||
|
||||
public function __construct(
|
||||
private readonly ContentModerationProcessingService $processing,
|
||||
private readonly ContentModerationSourceService $sources,
|
||||
private readonly ContentModerationActionLogService $actionLogs,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$limit = max(0, (int) ($this->option('limit') ?? 0));
|
||||
$fromId = max(0, (int) ($this->option('from-id') ?? 0));
|
||||
$status = trim((string) ($this->option('status') ?? 'pending'));
|
||||
$force = (bool) $this->option('force');
|
||||
$types = $this->selectedTypes();
|
||||
|
||||
$counts = [
|
||||
'rescanned' => 0,
|
||||
'updated' => 0,
|
||||
'auto_hidden' => 0,
|
||||
'missing_source' => 0,
|
||||
];
|
||||
|
||||
$query = ContentModerationFinding::query()->orderBy('id');
|
||||
|
||||
if ($status !== '') {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
if ($fromId > 0) {
|
||||
$query->where('id', '>=', $fromId);
|
||||
}
|
||||
|
||||
if ($types !== []) {
|
||||
$query->whereIn('content_type', array_map(static fn (ModerationContentType $type): string => $type->value, $types));
|
||||
}
|
||||
|
||||
if (! $force) {
|
||||
$query->where('status', ModerationStatus::Pending->value);
|
||||
}
|
||||
|
||||
if ($limit > 0) {
|
||||
$query->limit($limit);
|
||||
}
|
||||
|
||||
$query->chunkById(100, function ($findings) use (&$counts): bool {
|
||||
foreach ($findings as $finding) {
|
||||
$counts['rescanned']++;
|
||||
$rescanned = $this->processing->rescanFinding($finding, $this->sources);
|
||||
|
||||
if ($rescanned === null) {
|
||||
$counts['missing_source']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$counts['updated']++;
|
||||
if ($rescanned->is_auto_hidden) {
|
||||
$counts['auto_hidden']++;
|
||||
}
|
||||
|
||||
$this->actionLogs->log(
|
||||
$rescanned,
|
||||
'finding',
|
||||
$rescanned->id,
|
||||
'rescan',
|
||||
null,
|
||||
null,
|
||||
$rescanned->status->value,
|
||||
null,
|
||||
null,
|
||||
'Finding rescanned with the latest moderation rules.',
|
||||
['scanner_version' => $rescanned->scanner_version],
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 'id');
|
||||
|
||||
$this->table(['Metric', 'Count'], [
|
||||
['Rescanned', $counts['rescanned']],
|
||||
['Updated', $counts['updated']],
|
||||
['Auto-hidden', $counts['auto_hidden']],
|
||||
['Missing source', $counts['missing_source']],
|
||||
]);
|
||||
|
||||
$this->info('Content moderation rescan complete.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, ModerationContentType>
|
||||
*/
|
||||
private function selectedTypes(): array
|
||||
{
|
||||
$raw = trim((string) ($this->option('only') ?? ''));
|
||||
if ($raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$selected = \collect(explode(',', $raw))
|
||||
->map(static fn (string $value): string => trim(strtolower($value)))
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
$types = [];
|
||||
|
||||
if ($selected->contains('comments')) {
|
||||
$types[] = ModerationContentType::ArtworkComment;
|
||||
}
|
||||
|
||||
if ($selected->contains('descriptions')) {
|
||||
$types[] = ModerationContentType::ArtworkDescription;
|
||||
}
|
||||
|
||||
if ($selected->contains('titles') || $selected->contains('artwork_titles')) {
|
||||
$types[] = ModerationContentType::ArtworkTitle;
|
||||
}
|
||||
|
||||
if ($selected->contains('bios') || $selected->contains('user_bios')) {
|
||||
$types[] = ModerationContentType::UserBio;
|
||||
}
|
||||
|
||||
if ($selected->contains('profile-links') || $selected->contains('profile_links')) {
|
||||
$types[] = ModerationContentType::UserProfileLink;
|
||||
}
|
||||
|
||||
if ($selected->contains('collections') || $selected->contains('collection_titles')) {
|
||||
$types[] = ModerationContentType::CollectionTitle;
|
||||
$types[] = ModerationContentType::CollectionDescription;
|
||||
}
|
||||
|
||||
if ($selected->contains('stories') || $selected->contains('story_titles')) {
|
||||
$types[] = ModerationContentType::StoryTitle;
|
||||
$types[] = ModerationContentType::StoryContent;
|
||||
}
|
||||
|
||||
if ($selected->contains('cards') || $selected->contains('card_titles')) {
|
||||
$types[] = ModerationContentType::CardTitle;
|
||||
$types[] = ModerationContentType::CardText;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user