routmoute/routmoute-discord-bundle
A Laravel/PHP package that simplifies Discord integration, with configuration and service helpers for OAuth, webhooks, and bot workflows. Useful for adding Discord login, notifications, and community features to your app.
Start by reading the package's service provider and config file — with the 1.1.0 release adding a Discord API Service, the config is where you'll find the bot token, default guild/channel IDs, and any HTTP client options. Publish the config (php artisan vendor:publish) and confirm the service is auto-registered via package discovery before touching any code.
The first practical use case is sending a message to a channel from a queued job or a Laravel Notification. Wire up the token in .env, then call the service from tinker to validate credentials and permissions end-to-end before building anything on top of it. Discord auth failures (401) and permission failures (403) look identical from the application side until you inspect the response body, so prove the integration works in isolation first.
If the package ships a facade or a container binding, prefer resolving it from the container over instantiating the service directly — that keeps it swappable in tests and lets you bind a fake in the test suite.
Wrap it in a Notification channel. The most maintainable integration is a custom notification channel that delegates to the Discord service. Application code then dispatches notifications the same way it sends mail or Slack messages, and the Discord specifics stay in one class.
Queue everything. Discord calls are outbound HTTP with real rate limits. Dispatch a job (or use ShouldQueue on the notification) rather than calling the service inline in a request cycle. This also gives you free retry/backoff semantics via Laravel's queue configuration.
Separate webhook and bot-token paths. Webhooks are ideal for one-way operational alerts (deploy notices, error spikes) because they need no bot user, no guild membership, and no intents. Bot-token calls are for anything that needs to read state, manage channels, or post as a persistent identity. Keep two thin wrappers around the service if you use both.
Compose embeds explicitly. Build embed payloads from a small value object or array builder rather than inline arrays scattered across call sites. Embeds are where most payload-shape bugs appear, and a single builder makes the 25-field / 10-embed limits obvious at the point of construction.
Route by environment. Read channel IDs from config, not from hardcoded snowflakes, and use a separate channel per environment (or a suffix in the message) so staging alerts don't pollute production channels.
Test with Http::fake(). Assert the outbound request shape (endpoint, JSON body) rather than mocking the service class. That catches contract drift with the Discord API and validates your payload builder at the same time.
Rate limits are per-route, not per-token. Discord returns 429 with a retry_after field in the body and X-RateLimit-* headers. Laravel's default HTTP retry will not honor retry_after — configure backoff on the job, and log the bucket headers when debugging intermittent failures.
Snowflakes are strings. Channel, guild, and message IDs exceed 64-bit signed integer range in some languages but PHP handles them as ints on 64-bit builds — still treat them as strings everywhere, especially in JSON payloads and config, to avoid silent truncation on other runtimes.
Webhook URLs are credentials. Anyone with the URL can post to the channel. Keep them in .env/secrets manager, never in the database or logs, and rotate on exposure. If the package logs request URLs on failure, verify it isn't leaking the token or webhook URL into laravel.log.
Payload limits are enforced server-side. 2000 characters per message content, 4096 per embed description, 10 embeds per message. Truncate and chunk before sending; a 400 from Discord gives you a generic "Invalid Form Body" that is painful to trace back to a too-long field.
Markdown injection is real. User-supplied content containing @everyone, @here, or role mentions can ping an entire guild. Escape or sanitize interpolated values, and disable allowed mentions unless you explicitly need them.
Config caching will bite you. If your channel routing reads env() at runtime anywhere in your own code, php artisan config:cache in production will silently return null. Always route through config().
Extension points: the HTTP client and the payload builder are the two seams worth wrapping. If the service doesn't expose a way to inject a PendingRequest or override the base URI, keep your own thin adapter rather than reaching into the package internals — that adapter is also where you'll add logging, metrics, and a circuit breaker when Discord has an outage.
How can I help you explore Laravel packages today?