chore: remove unused PlatformRules registry and rule classes

The whole app/Ai/PlatformRules/ system was registered in AppServiceProvider
but never read in production code. Confirmed via grep across app/, resources/,
config/, database/, tests/ — only consumers were:

- AppServiceProvider::configurePlatformRules() registering everything in the
  Registry (which nothing then called)
- tests/Feature/Ai/PlatformRules/RegistryTest.php asserting the registrations
  it had just performed

The actual AI text agent (PostContentGenerator) reads the per-platform caps
straight from the Platform enum (maxContentLength / recommendedAiContentLength)
and feeds them into the prompt template — bypassing this layer entirely.

Removed:
- app/Ai/PlatformRules/ (12 files: Contract, Registry, 10 Rule classes)
- tests/Feature/Ai/PlatformRules/RegistryTest.php
- 11 use imports + boot() call + configurePlatformRules() method in
  AppServiceProvider
This commit is contained in:
Paulo Castellano 2026-05-11 21:08:43 -03:00
parent f217bba213
commit 7acc07976a
14 changed files with 0 additions and 516 deletions

View file

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class BlueskyRules implements Contract
{
public function platform(): Platform
{
return Platform::Bluesky;
}
public function specs(): array
{
return [
'max_content_length' => 300,
'max_images' => 4,
'max_videos' => 1,
'text_only_allowed' => true,
];
}
public function summary(): string
{
return 'Bluesky: 300 chars max. Up to 4 images or 1 video. Similar to X but more concise.';
}
}

View file

@ -1,19 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
interface Contract
{
public function platform(): Platform;
/**
* @return array<string, mixed>
*/
public function specs(): array;
public function summary(): string;
}

View file

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class FacebookRules implements Contract
{
public function platform(): Platform
{
return Platform::Facebook;
}
public function specs(): array
{
return [
'max_content_length' => 10000,
'max_images' => 10,
'text_only_allowed' => true,
'formats' => [
'post' => ['max_media' => 10, 'text_only_allowed' => true],
'reel' => ['aspect_ratio' => '9:16', 'video_only' => true, 'max_duration_seconds' => 90],
'story' => ['aspect_ratio' => '9:16', 'ephemeral' => true],
],
];
}
public function summary(): string
{
return <<<'TXT'
Facebook: caption up to 10k chars but short posts perform better. Text-only allowed.
- Post: flexible aspect ratio, up to 10 images/videos.
- Reel: vertical 9:16 video only, up to 90s.
- Story: vertical 9:16, single image/video, ephemeral.
TXT;
}
}

View file

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class InstagramRules implements Contract
{
public function platform(): Platform
{
return Platform::Instagram;
}
public function specs(): array
{
return [
'max_content_length' => 2200,
'max_images' => 10,
'max_videos' => 10,
'text_only_allowed' => false,
'formats' => [
'feed' => ['aspect_ratio' => '1:1 or 4:5', 'media_limit' => 10],
'carousel' => ['aspect_ratio' => '1:1 or 4:5', 'media_limit' => 10],
'reel' => ['aspect_ratio' => '9:16', 'video_only' => true, 'max_duration_seconds' => 90],
'story' => ['aspect_ratio' => '9:16', 'media_limit' => 1, 'ephemeral' => true],
],
];
}
public function summary(): string
{
return <<<'TXT'
Instagram: caption max 2200 chars.
- Feed: square (1:1) or portrait (4:5). Up to 10 images/videos.
- Carousel: 2-10 slides, all same aspect ratio. First slide is the hook.
- Reel: vertical 9:16 video only, up to 90s (30-60s performs best). Hook in first 1-3s.
- Story: vertical 9:16, single image/video, ephemeral (24h).
TXT;
}
}

View file

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class LinkedInRules implements Contract
{
public function platform(): Platform
{
return Platform::LinkedIn;
}
public function specs(): array
{
return [
'max_content_length' => 3000,
'max_images' => 1,
'max_videos' => 1,
'text_only_allowed' => true,
'formats' => [
'post' => ['max_media' => 1],
'carousel' => ['max_slides' => 20, 'optimal_slides' => '8-12', 'images_only' => true, 'document_based' => true],
],
];
}
public function summary(): string
{
return <<<'TXT'
LinkedIn: 3000 chars max. First 2-3 lines visible before "see more" — make them compelling.
Professional tone. Educational/how-to content performs well.
- Post: 1 image or 1 video.
- Carousel: PDF-based document, up to 20 slides (8-12 optimal), images only.
TXT;
}
}

View file

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class MastodonRules implements Contract
{
public function platform(): Platform
{
return Platform::Mastodon;
}
public function specs(): array
{
return [
'max_content_length' => 500,
'max_images' => 4,
'max_videos' => 1,
'text_only_allowed' => true,
];
}
public function summary(): string
{
return 'Mastodon: 500 chars max. Up to 4 images or 1 video. Community-focused, avoid commercial tone.';
}
}

View file

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class PinterestRules implements Contract
{
public function platform(): Platform
{
return Platform::Pinterest;
}
public function specs(): array
{
return [
'max_content_length' => 800,
'max_images' => 5,
'max_videos' => 1,
'text_only_allowed' => false,
'formats' => [
'pin' => ['aspect_ratio' => '2:3', 'media_limit' => 1, 'images_only' => true],
'video_pin' => ['aspect_ratio' => '9:16 or 2:3', 'video_only' => true, 'min_duration_seconds' => 4, 'max_duration_seconds' => 900],
'carousel' => ['aspect_ratio' => '2:3', 'max_slides' => 5, 'images_only' => true],
],
];
}
public function summary(): string
{
return <<<'TXT'
Pinterest: title max 100 chars, description max 500 chars. Text overlay on images helps discovery.
- Pin: portrait 2:3 (1000x1500px), single image.
- Video Pin: vertical 9:16 or portrait 2:3, 4s to 15 minutes.
- Carousel: portrait 2:3, up to 5 images.
TXT;
}
}

View file

@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class Registry
{
/**
* @var array<string, class-string<Contract>>
*/
private static array $map = [];
public static function for(Platform $platform): ?Contract
{
$class = self::$map[$platform->value] ?? null;
return $class ? new $class : null;
}
/**
* @param array<int, Platform> $platforms
* @return array<int, Contract>
*/
public static function forMany(array $platforms): array
{
$rules = [];
foreach ($platforms as $platform) {
$rule = self::for($platform);
if ($rule) {
$rules[] = $rule;
}
}
return $rules;
}
/**
* @param class-string<Contract> $ruleClass
*/
public static function register(Platform $platform, string $ruleClass): void
{
self::$map[$platform->value] = $ruleClass;
}
public static function clear(): void
{
self::$map = [];
}
}

View file

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class ThreadsRules implements Contract
{
public function platform(): Platform
{
return Platform::Threads;
}
public function specs(): array
{
return [
'max_content_length' => 500,
'max_images' => 10,
'max_videos' => 10,
'text_only_allowed' => true,
];
}
public function summary(): string
{
return 'Threads: 500 chars max. Up to 10 images/videos. Conversational tone. Ask questions to drive replies.';
}
}

View file

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class TikTokRules implements Contract
{
public function platform(): Platform
{
return Platform::TikTok;
}
public function specs(): array
{
return [
'max_content_length' => 2200,
'max_videos' => 1,
'text_only_allowed' => false,
'aspect_ratio' => '9:16',
'video_only' => true,
];
}
public function summary(): string
{
return 'TikTok: vertical 9:16 video only. 15-60s performs best. Hook in first 1-2s. Trendy, authentic style. Caption max 2200 chars.';
}
}

View file

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class XRules implements Contract
{
public function platform(): Platform
{
return Platform::X;
}
public function specs(): array
{
return [
'max_content_length' => 280,
'max_images' => 4,
'max_videos' => 1,
'text_only_allowed' => true,
'best_aspect_ratio' => '16:9',
];
}
public function summary(): string
{
return 'X (Twitter): 280 chars max. Up to 4 images OR 1 video. 16:9 landscape performs best. Concise, punchy copy.';
}
}

View file

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Ai\PlatformRules;
use App\Enums\SocialAccount\Platform;
class YouTubeRules implements Contract
{
public function platform(): Platform
{
return Platform::YouTube;
}
public function specs(): array
{
return [
'max_content_length' => 5000,
'max_videos' => 1,
'text_only_allowed' => false,
'aspect_ratio' => '9:16',
'video_only' => true,
'max_duration_seconds' => 60,
'requires_title' => true,
];
}
public function summary(): string
{
return 'YouTube Shorts: vertical 9:16 video only, up to 60s. Hook immediately. Loop-friendly content helps. Description max 5000 chars. Title required.';
}
}

View file

@ -4,19 +4,7 @@
namespace App\Providers;
use App\Ai\PlatformRules\BlueskyRules;
use App\Ai\PlatformRules\FacebookRules;
use App\Ai\PlatformRules\InstagramRules;
use App\Ai\PlatformRules\LinkedInRules;
use App\Ai\PlatformRules\MastodonRules;
use App\Ai\PlatformRules\PinterestRules;
use App\Ai\PlatformRules\Registry;
use App\Ai\PlatformRules\ThreadsRules;
use App\Ai\PlatformRules\TikTokRules;
use App\Ai\PlatformRules\XRules;
use App\Ai\PlatformRules\YouTubeRules;
use App\Ai\Providers\ExtendedGeminiProvider;
use App\Enums\SocialAccount\Platform;
use App\Listeners\StripeEventListener;
use App\Models\AccessToken;
use App\Models\Account;
@ -97,7 +85,6 @@ public function boot(): void
$this->configureDefaults();
$this->configureMorphMap();
$this->configureAi();
$this->configurePlatformRules();
$this->configurePostHog();
$this->configureRateLimiting();
$this->configureSocialite();
@ -136,31 +123,6 @@ protected function configureAi(): void
});
}
protected function configurePlatformRules(): void
{
$map = [
Platform::Instagram->value => InstagramRules::class,
Platform::InstagramFacebook->value => InstagramRules::class,
Platform::Facebook->value => FacebookRules::class,
Platform::X->value => XRules::class,
Platform::TikTok->value => TikTokRules::class,
Platform::YouTube->value => YouTubeRules::class,
Platform::LinkedIn->value => LinkedInRules::class,
Platform::LinkedInPage->value => LinkedInRules::class,
Platform::Threads->value => ThreadsRules::class,
Platform::Pinterest->value => PinterestRules::class,
Platform::Bluesky->value => BlueskyRules::class,
Platform::Mastodon->value => MastodonRules::class,
];
foreach ($map as $value => $class) {
Registry::register(
Platform::from($value),
$class,
);
}
}
protected function configureMorphMap(): void
{
Relation::enforceMorphMap([

View file

@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
use App\Ai\PlatformRules\Contract;
use App\Ai\PlatformRules\Registry;
use App\Ai\PlatformRules\XRules;
use App\Enums\SocialAccount\Platform;
test('registry returns rule instance for every supported platform', function (Platform $platform) {
$rules = Registry::for($platform);
expect($rules)
->toBeInstanceOf(Contract::class)
->and(strlen($rules->summary()))->toBeGreaterThan(30)
->and($rules->specs())->toBeArray();
})->with([
[Platform::Instagram],
[Platform::InstagramFacebook],
[Platform::Facebook],
[Platform::X],
[Platform::TikTok],
[Platform::YouTube],
[Platform::LinkedIn],
[Platform::LinkedInPage],
[Platform::Threads],
[Platform::Pinterest],
[Platform::Bluesky],
[Platform::Mastodon],
]);
test('forMany returns rules for the given platforms in order', function () {
$rules = Registry::forMany([Platform::X, Platform::Instagram, Platform::TikTok]);
expect($rules)->toHaveCount(3);
expect($rules[0]->platform())->toBe(Platform::X);
expect($rules[1]->platform())->toBe(Platform::Instagram);
expect($rules[2]->platform())->toBe(Platform::TikTok);
});
test('forMany skips platforms with no registered rules', function () {
Registry::clear();
Registry::register(Platform::X, XRules::class);
$rules = Registry::forMany([Platform::X, Platform::Instagram]);
expect($rules)->toHaveCount(1);
expect($rules[0]->platform())->toBe(Platform::X);
});
test('X rules summary mentions 280 char limit', function () {
expect(Registry::for(Platform::X)->summary())->toContain('280');
});
test('Instagram rules summary mentions 9:16 for Reel', function () {
expect(Registry::for(Platform::Instagram)->summary())->toContain('9:16');
});
test('TikTok rules specs indicate video only', function () {
expect(Registry::for(Platform::TikTok)->specs()['video_only'])->toBeTrue();
});