Skip to content

rodrigocaetanooficial/PHPInstagramAutoPoster

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

48 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Instagram Auto-Poster

A self-hosted PHP application that automatically posts AI-generated images to Instagram Feed and Stories. Designed for hands-free operation via cron jobs, with a real-time dashboard for monitoring.

PHP 8.1+ License MIT Instagram Graph API OpenRouter Gemma

Follow @digital_dreams_ai


Table of Contents


Features

  • Automated Posting β€” Runs via cron, no manual intervention needed
  • AI-Generated Captions β€” Uses OpenRouter (Gemma 3 Vision) to analyze each image and write poetic captions
  • Dual Posting β€” Publishes to both Instagram Feed and Stories simultaneously
  • Aspect Ratio Correction β€” Automatically adds black bars to images outside Instagram's ratio requirements (4:5 to 1.91:1)
  • Format Conversion β€” Converts PNG/WebP to JPEG for Instagram compatibility
  • Error Recovery β€” Recovers pending stories if publishing fails mid-process
  • Real-Time Dashboard β€” Monitor posts, errors, logs, and trigger manual runs
  • Atomic Publishing β€” Feed and Story are published together; if Feed fails, Story is skipped
  • File-Based Storage β€” No database required; everything runs on flat files
  • Mutex Locking β€” Prevents concurrent poster runs

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Cron Job  │────▢│  cron.php    │────▢│   App.php       β”‚
β”‚  (hourly)   β”‚     β”‚  (CLI entry) β”‚     β”‚  (Orchestrator) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                  β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚                             β”‚                             β”‚
              β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
              β”‚ ImageQueue β”‚              β”‚  GroqAI      β”‚              β”‚  Instagram   β”‚
              β”‚  (scanner) β”‚              β”‚  (OpenRouter)β”‚              β”‚  (Graph API) β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              Dashboard (Web UI)                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Stats   β”‚  β”‚  Posts   β”‚  β”‚  Errors  β”‚  β”‚   Log    β”‚  β”‚ Manual Run   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The posting flow is a single CLI process with no HTTP intermediaries:

  1. Cron triggers cron.php directly via CLI
  2. App acquires a mutex lock to prevent concurrent runs
  3. ImageQueue selects a random valid image from the images/ directory
  4. ImageProcessor fixes aspect ratio and converts to JPEG
  5. GroqAI (via OpenRouter) generates a caption using the Gemma 3 vision model
  6. Instagram creates Feed and Story containers, waits for processing, then publishes
  7. Posted images are deleted (configurable) and results are logged

Requirements

  • PHP 8.1+ with the following extensions:
    • gd (image processing)
    • curl (API requests)
    • json (data handling)
    • mbstring (string handling)
  • Web Server: Apache or Nginx (for dashboard access)
  • Instagram Business Account connected to a Facebook Page
  • Meta Developer App with instagram_basic and instagram_content_publish permissions
  • OpenRouter API Key (free tier available at openrouter.ai)
  • Cron access (for scheduled posting)

Installation

1. Clone the Repository

git clone https://github.com/rodrigocaetanooficial/PHPInstagramAutoPoster.git
cd PHPInstagramAutoPoster

2. Configure

cp config-sample.php config.php
nano config.php

Fill in your API credentials (see Configuration below).

3. Set Permissions

chmod 755 images/
chmod 777 logs/

The logs/ directory must be writable by the web server user and the cron user.

4. Upload Images

Place your images in the images/ directory. Supported formats: JPG, JPEG, PNG, WebP. Minimum size: 400Γ—400px.

5. Set Up Cron

crontab -e

Add this line to run every hour:

0 * * * * /usr/bin/php /path/to/cron.php >> /path/to/logs/cron.log 2>&1

6. Access the Dashboard

Open https://yourdomain.com in your browser. Default credentials:

  • Username: admin
  • Password: (set your own in config.php)

cPanel Hosting Setup

This section covers everything you need to deploy on shared hosting with cPanel (e.g., HostGator, Hostinger, ViawWeb, etc.).

Step 1 β€” Upload Files

  1. In cPanel β†’ File Manager, navigate to public_html/ (or a subdomain folder)
  2. Upload all project files, except config.php (which contains secrets)
  3. Create config.php manually in cPanel's File Manager using the contents of config-sample.php

Alternatively, use FTP (FileZilla, etc.) to upload all files at once.

Step 2 β€” Set Folder Permissions

In cPanel β†’ File Manager, right-click each folder and choose "Change Permissions":

Folder Permission
images/ 755
images/_stories/ 755
logs/ 777

Or via cPanel β†’ Terminal (if available):

chmod 755 images/ images/_stories/
chmod 777 logs/

Step 3 β€” Verify PHP Version & Extensions

  1. Go to cPanel β†’ MultiPHP Manager (or PHP Selector)
  2. Set PHP version to 8.1 or higher for your domain
  3. Go to cPanel β†’ PHP Extensions (or Select PHP Extensions) and ensure these are enabled:
    • βœ… gd
    • βœ… curl
    • βœ… json
    • βœ… mbstring

Step 4 β€” Set Up the Cron Job

  1. In cPanel β†’ Cron Jobs
  2. Set "Common Settings" to Once Per Hour (or custom: 0 * * * *)
  3. In the Command field, enter:
/usr/local/bin/php /home/YOUR_CPANEL_USER/public_html/cron.php >> /home/YOUR_CPANEL_USER/public_html/logs/cron.log 2>&1

⚠️ Replace YOUR_CPANEL_USER with your actual cPanel username (visible in the top-right of cPanel). The PHP path may vary β€” try /usr/bin/php or /usr/local/php81/bin/php if the above doesn't work.

To find the correct PHP path via cPanel Terminal:

which php

Step 5 β€” Configure config.php

Fill in all values in config.php on the server. Key fields:

define('IMAGES_DIR', '/home/YOUR_CPANEL_USER/public_html/images');
define('IMAGES_URL', 'https://yourdomain.com/images');
define('LOGS_DIR',   '/home/YOUR_CPANEL_USER/public_html/logs');

⚠️ Use absolute server paths for IMAGES_DIR and LOGS_DIR. You can find the full path in cPanel β†’ File Manager (shown in the address bar or via Terminal: pwd).

Step 6 β€” Protect Sensitive Files

The included .htaccess already blocks direct access to config.php, src/, and logs/. Verify it is uploaded and that Apache's mod_rewrite is enabled (it is on most cPanel hosts by default).

To confirm protection is working, open https://yourdomain.com/config.php in a browser β€” you should receive a 403 Forbidden error.

Step 7 β€” Test the Setup

In cPanel β†’ Terminal (or via SSH):

php /home/YOUR_CPANEL_USER/public_html/cron.php

Then check the log:

tail -f /home/YOUR_CPANEL_USER/public_html/logs/debug.log

A successful run will end with: === Poster run completed successfully ===


Configuration

All settings are in config.php. Here's what each section does:

Facebook / Instagram Graph API

Constant Description
FB_PAGE_ACCESS_TOKEN System User Access Token (permanent) or Long-Lived Page Access Token
IG_BUSINESS_ID Instagram Business Account ID
GRAPH_API_VERSION Graph API version (default: v22.0)

Recommended: System User Token (never expires)

  1. In Meta Business Suite β†’ Business Settings β†’ System Users
  2. Create a System User and assign it to your Page and Instagram account
  3. Generate a token with instagram_basic and instagram_content_publish permissions
  4. This token does not expire β€” ideal for automated posting

Alternative: Long-lived Page Access Token (expires in ~60 days)

  1. Go to developers.facebook.com/tools/explorer
  2. Select your App and Page, add instagram_basic + instagram_content_publish permissions
  3. Generate and exchange for a long-lived token

OpenRouter AI (Caption Generation)

Constant Description
GROQ_API_KEY Your API key from openrouter.ai
GROQ_MODEL Vision model (default: google/gemma-3-4b-it:free)
GROQ_API_URL OpenRouter endpoint (default: https://openrouter.ai/api/v1/chat/completions)

Note: Despite the GROQ_ prefix (kept for backwards compatibility), this now uses the OpenRouter API with Google's Gemma 3 vision model. Get a free key at openrouter.ai.

Paths

Constant Description
IMAGES_DIR Absolute server path to the images directory
IMAGES_URL Public HTTPS URL where images are accessible (must be reachable by Instagram's servers)
LOGS_DIR Absolute server path to the logs directory

Posting Behavior

Constant Default Description
DELETE_POSTED_IMAGE true Delete original image after posting
MAX_LOG_ENTRIES 100 Maximum entries in JSON log files
CONTAINER_RETRY_COUNT 3 Retries for container creation
CONTAINER_RETRY_DELAY 5 Seconds between retries
STORY_PUBLISH_DELAY 15 Seconds to wait before publishing Story
CONTAINER_MAX_WAIT 90 Max seconds to wait for container processing

Image Validation

Constant Default Description
ALLOWED_EXTENSIONS jpg, jpeg, png, webp Accepted file extensions
FEED_MIN_RATIO 0.8 Minimum aspect ratio (4:5 portrait)
FEED_MAX_RATIO 1.91 Maximum aspect ratio (1.91:1 landscape)
MIN_IMAGE_SIZE 400 Minimum width/height in pixels

Dashboard Authentication

Constant Description
DASHBOARD_USER Dashboard username
DASHBOARD_PASSWORD_HASH Bcrypt hash of the password

Generate a password hash:

php -r "echo password_hash('your_password', PASSWORD_BCRYPT);"

Cron Setup

Standard Cron (Linux/VPS)

0 * * * * /usr/bin/php /var/www/html/cron.php >> /var/www/html/logs/cron.log 2>&1

cPanel Cron Job

In cPanel β†’ Cron Jobs, set frequency to 0 * * * * and command to:

/usr/local/bin/php /home/YOUR_CPANEL_USER/public_html/cron.php >> /home/YOUR_CPANEL_USER/public_html/logs/cron.log 2>&1

Testing Manually

php cron.php

Checking Results

tail -n 50 logs/debug.log
tail -n 10 logs/posts.json
tail -n 10 logs/errors.json

Dashboard

The dashboard provides real-time monitoring:

Section Description
Stats Cards Image count, total posts, errors (24h), next cron run
Manual Run Trigger a post immediately with CSRF protection
Run Log Live debug log showing every step of the posting process
Recent Posts Last 10 posts with timestamps, captions, and post IDs
Recent Errors Last 10 errors with context for debugging
Trigger Log Diagnostic log for manual run attempts

Diagnostic Buttons

Button Purpose
πŸ”“ Clear Lock Removes stale mutex lock file
πŸ” Check Lock Shows current lock status and age
πŸ§ͺ Test Lock Tests filesystem permissions for fopen/flock

Image Processing Pipeline

Original Image
     β”‚
     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Aspect Ratio Fix   β”‚  Adds black bars if ratio is outside 0.8–1.91
β”‚  (ImageProcessor)   β”‚  Too tall β†’ pillarbox | Too wide β†’ letterbox
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AI Caption Gen     β”‚  Sends image URL to OpenRouter (Gemma 3 vision)
β”‚  (GroqAI class)     β”‚  Returns poetic caption (max 150 chars)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Convert to JPEG    β”‚  Converts PNG/WebP to 92% quality JPEG
β”‚  (ImageProcessor)   β”‚  Sanitizes filename for URL safety
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Story Image        β”‚  Creates 1080Γ—1920 story with letterboxing
β”‚  (ImageProcessor)   β”‚  Saves to images/_stories/
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Instagram API      β”‚  1. Create Feed container β†’ wait β†’ publish
β”‚  (Instagram)        β”‚  2. Create Story container β†’ wait β†’ publish
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Cleanup            β”‚  Deletes posted images (if configured)
β”‚                     β”‚  Logs result to posts.json
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Security

Feature Implementation
Password Hashing Bcrypt via password_verify()
CSRF Protection Session-based tokens with hash_equals()
Rate Limiting IP-based login throttling (5 attempts, 15min lockout)
Session Security HttpOnly cookies, SameSite=Strict, strict mode
Input Sanitization htmlspecialchars() on all output
Filename Sanitization Strips URL-unsafe characters from filenames
Access Control .htaccess blocks direct access to config, src, logs
Security Headers X-Content-Type-Options, X-Frame-Options, CSP, etc.

Important

  • Never commit config.php β€” it contains API keys and passwords
  • Keep IMAGES_URL public β€” Instagram's servers must be able to fetch images
  • Use HTTPS β€” required for session cookies and API security

Troubleshooting

"Another instance is running"

The mutex lock file is stale. Clear it:

rm /path/to/logs/poster.lock

Or use the πŸ”“ Clear Lock button in the dashboard.

Permission denied on logs

chmod -R 777 /path/to/logs/

On cPanel, do this via File Manager β†’ Change Permissions.

Images not posting

  1. Check logs/debug.log for error details
  2. Verify IMAGES_URL is publicly accessible (Instagram must fetch it via HTTPS)
  3. Ensure FB_PAGE_ACCESS_TOKEN is valid and not expired
  4. Check logs/errors.json for API errors

Token expired (OAuthException code 190)

This happens with short-lived tokens. Solutions:

  • Best: Use a System User Token (permanent, never expires) β€” see Configuration
  • Alternative: Regenerate a long-lived token from the Graph API Explorer and update FB_PAGE_ACCESS_TOKEN in config.php

504 Gateway Timeout

The posting process takes 2–3 minutes. Increase your server timeouts:

On cPanel β€” Add to .htaccess:

php_value max_execution_time 300

Nginx (add to your site config):

proxy_read_timeout 300s;

Apache (apache2.conf):

Timeout 300

PHP-FPM (pool config):

request_terminate_timeout = 300

Cron not running on cPanel

  1. Verify the PHP path: in cPanel β†’ Terminal, run which php
  2. Check that cron output is being written: look in logs/cron.log
  3. Ensure IMAGES_DIR and LOGS_DIR use absolute paths in config.php

Project Structure

β”œβ”€β”€ config-sample.php      # Configuration template (safe to commit)
β”œβ”€β”€ config.php             # Your configuration (NEVER commit)
β”œβ”€β”€ cron.php               # CLI entry point for cron jobs
β”œβ”€β”€ index.php              # Dashboard entry point
β”œβ”€β”€ login.php              # Login page
β”œβ”€β”€ logout.php             # Logout handler
β”œβ”€β”€ api.php                # JSON API for dashboard AJAX calls
β”œβ”€β”€ .htaccess              # Apache security rules
β”‚
β”œβ”€β”€ assets/
β”‚   β”œβ”€β”€ app.js             # Dashboard JavaScript
β”‚   └── style.css          # Dashboard CSS (Neon 80s dark theme)
β”‚
β”œβ”€β”€ images/                # Drop your images here
β”‚   └── _stories/          # Auto-generated story images
β”‚
β”œβ”€β”€ logs/
β”‚   β”œβ”€β”€ debug.log          # Plain text debug log
β”‚   β”œβ”€β”€ posts.json         # Structured post history
β”‚   β”œβ”€β”€ errors.json        # Structured error history
β”‚   β”œβ”€β”€ trigger.log        # Manual run diagnostics
β”‚   └── poster.lock        # Mutex lock file
β”‚
└── src/
    β”œβ”€β”€ App.php            # Main orchestrator
    β”œβ”€β”€ Auth.php           # Authentication, CSRF, rate limiting
    β”œβ”€β”€ GroqAI.php         # AI caption generation via OpenRouter API
    β”œβ”€β”€ ImageProcessor.php # GD image manipulation
    β”œβ”€β”€ ImageQueue.php     # Image scanning and selection
    β”œβ”€β”€ Instagram.php      # Instagram Graph API client
    └── Logger.php         # File-based logging

License

MIT License. See LICENSE for details.

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors