trypost/app/Exceptions/Social/DiscordPublishException.php
Paulo Castellano c2dd4515b2 feat(channels): add Discord as a social channel
Connect a Discord server via OAuth (bot authorization) and schedule/publish
messages to its channels, with mentions and rich embeds.

- Connect: custom Socialite Discord provider (bot scope) maps the authorized
  guild to a SocialAccount; throws if no server was authorized.
- Publish: DiscordPublisher posts via the global bot token, validates the chosen
  channel belongs to the connected guild (anti cross-guild), optimizes media,
  builds allowed_mentions only from explicit mention chips (no accidental pings),
  and renders rich embeds.
- Compose: per-post channel picker (live lookup), mention autocomplete and an
  embed editor, gated by a required-channel compliance rule; Discord post preview.
- Enum/config/content-type wiring, ConnectionVerifier health check, throttled
  lookup endpoints, i18n (en/es/pt-BR), and tests.

Operators must create a Discord application and set DISCORD_CLIENT_ID,
DISCORD_CLIENT_SECRET, DISCORD_BOT_TOKEN and DISCORD_CLIENT_REDIRECT.
2026-06-16 14:44:00 -03:00

89 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Exceptions\Social;
use Illuminate\Http\Client\Response;
class DiscordPublishException extends SocialPublishException
{
public static function fromApiResponse(mixed $response): static
{
/** @var Response $response */
$status = $response->status();
$rawResponse = $response->body();
$code = (int) data_get($response->json(), 'code');
$message = (string) data_get($response->json(), 'message', 'An unknown Discord error occurred.');
// Missing access / missing permissions: the bot can't see or post in the channel.
if (in_array($code, [50001, 50013], true) || $status === 403) {
return new static(
userMessage: "The bot can't post in this channel. Make sure it has access and permission to send messages there.",
category: ErrorCategory::Permission,
platformErrorCode: (string) $code,
rawResponse: $rawResponse,
);
}
// Unknown channel — it was deleted or the id is stale.
if ($code === 10003) {
return new static(
userMessage: 'That Discord channel no longer exists. Pick a different channel.',
category: ErrorCategory::Permission,
platformErrorCode: (string) $code,
rawResponse: $rawResponse,
);
}
// Attachment too large.
if ($code === 40005 || $status === 413) {
return new static(
userMessage: "A media file exceeds Discord's upload size limit.",
category: ErrorCategory::MediaFormat,
platformErrorCode: (string) $code,
rawResponse: $rawResponse,
);
}
// 401: the configured bot token is invalid (operator-level misconfiguration).
if ($status === 401) {
return new static(
userMessage: 'Discord rejected the bot token. Check the DISCORD_BOT_TOKEN configuration.',
category: ErrorCategory::Permission,
platformErrorCode: (string) $status,
rawResponse: $rawResponse,
);
}
if ($status === 429) {
return new static(
userMessage: 'Discord rate limit reached. Please try again shortly.',
category: ErrorCategory::RateLimit,
platformErrorCode: (string) $status,
rawResponse: $rawResponse,
);
}
if ($status >= 500) {
return new static(
userMessage: 'Discord is temporarily unavailable. Please try again later.',
category: ErrorCategory::ServerError,
platformErrorCode: (string) $status,
rawResponse: $rawResponse,
);
}
return new static(
userMessage: $message,
category: ErrorCategory::Unknown,
platformErrorCode: (string) $code,
rawResponse: $rawResponse,
);
}
public function platform(): string
{
return 'discord';
}
}