Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Social Auto Post Laravel Package

hamzahassanm/laravel-social-auto-post

A Laravel package for automatically publishing your posts to social media. Schedule and auto-share content across connected platforms, queue posts, and integrate social posting into your app with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This package wraps platform-specific social APIs behind a shared SocialMediaService base class. v2.6.0 is a breaking release — if you are upgrading, read the Gotchas section before touching code.

Install / upgrade

composer require <vendor>/<package>:^2.6
php artisan vendor:publish --tag=social-media-config   # confirm tag name in the package README

Where to look first

  1. config/social-media.php — credentials, retry counts, timeouts, and any media host allowlist.
  2. SocialMediaService — the base class now owning validateTextUrl(), validateMediaInput(), and downloadMediaToTempFile(). This is the contract your custom platform drivers extend.
  3. SafeMediaFetcher — the only supported path for pulling remote media. Read it before you debug a media failure.

First use case: post with a remote image

use Vendor\SocialMedia\Services\InstagramService;

$service = app(InstagramService::class);

$post = $service->post(
    text: 'Shipped v2.6.0 🚀',
    media: ['https://cdn.example.com/hero.jpg'],
);

Remote URLs are fetched through SafeMediaFetcher::fetch(), validated, streamed to a temp file via downloadMediaToTempFile(), uploaded, then cleaned up. That is the happy path — everything else in this assessment is about the edges.

Implementation Patterns

Let the retry layer do its job — catch narrowly The new exception hierarchy exists so your code can make a decision instead of guessing:

use Vendor\SocialMedia\Exceptions\RateLimitException;
use Vendor\SocialMedia\Exceptions\RetryableException;

try {
    $service->post($text, $media);
} catch (RateLimitException $e) {
    // Platform told us to slow down. Queue it, don't hammer it.
    PostToSocial::dispatch($payload)->delay($e->retryAfter ?? now()->addMinutes(5));
} catch (RetryableException $e) {
    // 5xx / timeout. Retries were already exhausted — this is a real outage.
    report($e);
    $this->release(60);
}
// 4xx (validation, bad token, unsupported media) fails fast and is *not* RetryableException.
// Let it bubble or catch the package's base exception explicitly.

Do not wrap calls in catch (\Throwable) and retry yourself. You will stack your backoff on top of the package's exponential backoff + jitter, and turn a 30-second failure into a five-minute one.

Prefer queue jobs over inline calls Because 5xx now retries with exponential backoff, a failing post can occupy a worker for a while. Run posts from a job with a $tries that matches (or is lower than) the package's internal retry count so you don't multiply attempts:

public int $tries = 3;      // align with config('social-media.retry.max_attempts')
public int $backoff = 30;   // package handles jitter; keep yours coarse

Reuse the consolidated validators in custom drivers validateTextUrl() and validateMediaInput() exist so platform services stop re-implementing URL and media checks. If you maintain your own driver:

class MastodonService extends SocialMediaService
{
    public function post(string $text, array $media = []): PostResult
    {
        $text  = $this->validateTextUrl($text);        // normalizes/validates links in body copy
        $files = $this->validateMediaInput($media);    // rejects unsupported types, enforces limits

        // ... platform call
    }
}

Delete any local copies of that logic — divergence from the base class is how you end up shipping an SSRF hole through your own driver.

Media input handling downloadMediaToTempFile() is now the single funnel. Pass URLs or local paths through validateMediaInput() first and let the base class resolve them; don't hand-roll file_get_contents() on a user-supplied URL. If you were calling the removed downloadFile(), the migration is mechanical:

// Before (removed in 2.6.0)
$path = $service->downloadFile($url);

// After
$path = $service->downloadMediaToTempFile($url); // memory-safe, streamed to disk

Wrap the call in a try/finally (or use the returned temp path inside a scoped block) if you are composing several uploads in one job — temp files are yours to clean up once you leave the service method.

Async/batch posting For fan-out to N platforms, one job per platform beats one job looping platforms. A single 4xx on platform three shouldn't re-run the two that already succeeded, and per-platform retry budgets stay independent.

Gotchas and Tips

Upgrade checklist (these will bite you in production)

  • SocialMediaService::downloadFile() is gone, not deprecated. Grep your app and your own drivers: grep -rn "downloadFile(" app/.
  • SafeMediaFetcher::execute() is now private. Any direct call fails with a visibility error at runtime, not at boot — it will not show up until that path executes. Switch to fetch().
  • The $fetchHandler testing backdoor was removed. Any test that injected a fake handler is now broken; see the testing note below.

Retry behavior changes your timing assumptions 4xx calls that used to burn retries now fail fast. If you had alerting tuned to "post failed after 3 attempts / ~90s", 4xx alerts will now fire almost immediately and 5xx alerts will fire later (backoff + jitter). Re-baseline your dashboards before you trust them.

Jitter makes tests slow and flaky Tests that exercise the retry path will actually sleep. Use Http::fake() to return the 5xx sequence and Queue::fake()/Bus::fake() around the job, or override the retry config in phpunit.xml (max_attempts => 1, base delay 0). Never assert on exact elapsed time.

Testing media fetches without the backdoor Bind a fake into the container, or fake at the HTTP layer:

Http::fake([
    'cdn.example.com/*' => Http::response($bytes, 200, ['Content-Type' => 'image/jpeg']),
]);

This is the supported replacement for $fetchHandler and exercises the real validation path.

SSRF hardening will reject hosts you actually use SafeMediaFetcher validates every resolved A/AAAA record, so these now fail:

  • Internal/private IPs, localhost, link-local, and metadata endpoints (correctly).
  • Partner CDNs that resolve to private ranges or use split-horizon DNS inside your VPC.
  • Hosts behind an internal proxy, if the proxy is addressed by hostname resolving internally.

If legitimate traffic breaks post-upgrade, do not reach for a bypass — there is no public one now. Use the config allowlist (confirm the key in config/social-media.php) and keep it tight. Prefer mirroring partner media to your own public bucket over allowlisting an internal host.

DNS rebinding protection costs a lookup per URL Every media fetch resolves and validates the full record set before downloading. High-volume batch posting sees measurable DNS latency — enable whatever resolver cache your infra offers, and avoid re-validating the same URL in a loop (resolve/fetch once, reuse the temp file for multiple platforms where the API allows).

IPv6 zone identifiers fe80::1%eth0-style inputs are now explicitly rejected. If you accept media URLs from end users, expect a small uptick in validation errors from odd inputs — surface a clean "unsupported media URL" message rather than a raw exception.

OOM is fixed, disk is not downloadMediaToTempFile() solves large-file memory crashes, but the file still lands on local disk. Check upload_max_filesize/post_max_size are irrelevant here — what matters is free space in sys_get_temp_dir() (or your configured temp path) and that cleanup actually runs when a later step throws. Constrain input size in validateMediaInput() rather than relying on the downloader to stop.

Fail-fast means fail loudly A 4xx no longer gets retried into oblivion, so a bad token or expired credential surfaces on attempt one. Good for latency, but it means your error reporting must actually be hooked up before you deploy — silence used to be masked by retries.

Log the exception class, not just the message RetryableException vs RateLimitException vs a plain 4xx failure are three different operational problems. Include get_class($e) in your log context so you can tell "platform is down" from "we're being throttled" from "our payload is wrong" without reading stack traces.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log