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:
parent
f217bba213
commit
7acc07976a
14 changed files with 0 additions and 516 deletions
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = [];
|
||||
}
|
||||
}
|
||||
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -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([
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
Loading…
Reference in a new issue