Laravel 12 project (PHP + Redis only, no SQL database) that sends Telegram messages via a queue job with Redis-enforced rate limits:
- Global: no more than 30 messages per second across the entire bot
- Per chat: no more than 20 messages per minute per
chat_id
Limits are enforced via job middleware using Laravel's RateLimiter facade (Redis-backed). No custom polling or env() in application code; configuration is read only from config files.
- Docker and Docker Compose
- (Optional) Telegram Bot Token for real sends
cd smsManagercopy .env.example .env
php artisan key:generateEdit .env and set TELEGRAM_BOT_TOKEN from @BotFather (optional; without it, jobs run but skip sending).
Never commit .env — it is listed in .gitignore; use .env.example as a template only.
docker compose up -dThis starts Redis and the queue worker (app). The worker processes jobs and sends messages to Telegram.
# From host (jobs go to Redis; worker in container processes them)
docker compose run --rm app php artisan telegram:dispatch-test 100Or target one chat:
docker compose run --rm app php artisan telegram:dispatch-test 10 --chat=-5230897744Messages appear in Telegram as the worker processes the queue (rate limits are applied per job).
| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token from BotFather (optional for testing) |
TELEGRAM_GLOBAL_LIMIT_PER_SECOND |
Max messages per second globally (default: 30) |
TELEGRAM_CHAT_LIMIT_PER_MINUTE |
Max messages per minute per chat (default: 20) |
REDIS_HOST |
Redis host (redis in Docker, 127.0.0.1 for local Redis) |
REDIS_CLIENT |
phpredis in Docker, predis when running PHP locally without the redis extension |
| Path | Description |
|---|---|
app/Jobs/SendMessageJob.php |
Queue job: sends via TelegramService; rate limiting via middleware |
app/Jobs/Middleware/TelegramRateLimitMiddleware.php |
Job middleware: Laravel RateLimiter (Redis) — 30/s global, 20/min per chat |
app/Services/TelegramService.php |
Telegram API abstraction: sendMessage() using config only |
app/Console/Commands/DispatchTelegramTestCommand.php |
Artisan command to dispatch N test jobs |
config/services.php |
Telegram token and rate-limit values (from env; never env() in app code) |
config/telegram.php |
Default chat IDs for test command |
config/queue.php, config/redis.php |
Queue and Redis |
docker-compose.yml |
App (queue worker) + Redis |
Dockerfile |
PHP 8.4, Redis extension; required for docker compose build |
- Global:
RateLimiter::hit('telegram-global:' . time(), 2)with max 30 attempts; over limit → job released with 1s delay. - Per chat:
RateLimiter::hit('telegram-chat:{chat_id}:{minute}', 120)with max 20 attempts; over limit → job released until next minute.
Uses Laravel's built-in Illuminate\Support\Facades\RateLimiter (Redis cache driver). No SQL database.
# Start app + Redis
docker compose up -d
# Stop
docker compose down
# App logs (queue worker)
docker compose logs -f app
# Dispatch 100 jobs (default: alternating chat IDs)
docker compose run --rm app php artisan telegram:dispatch-test 100
# Dispatch 5 jobs to one chat
docker compose run --rm app php artisan telegram:dispatch-test 5 --chat=YOUR_CHAT_ID- Send a message to your bot (e.g.
/start). - Open:
https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates - In the JSON, use
result[].message.chat.id(e.g.-5230897744for a group).
Use that value as --chat=... or in SendMessageJob::dispatch($chatId, $text).