trypost/app/Http/Controllers/App/DiscordController.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

59 lines
1.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\App;
use App\Enums\SocialAccount\Platform as SocialPlatform;
use App\Http\Controllers\Controller;
use App\Http\Requests\App\Discord\IndexDiscordMentionRequest;
use App\Models\SocialAccount;
use App\Services\Social\Discord\DiscordClient;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class DiscordController extends Controller
{
public function __construct(private DiscordClient $discord) {}
public function channels(Request $request, SocialAccount $account): JsonResponse
{
$this->authorizeDiscordAccount($request, $account);
// Degrade to an empty list on a transient Discord outage — the picker
// shows "no channels" rather than erroring; the publish path still
// retries (channels() throws there).
return response()->json([
'channels' => rescue(
fn () => $this->discord->channels((string) $account->platform_user_id),
[],
report: false,
),
]);
}
public function mentions(IndexDiscordMentionRequest $request, SocialAccount $account): JsonResponse
{
$this->authorizeDiscordAccount($request, $account);
return response()->json([
'mentions' => $this->discord->mentions(
(string) $account->platform_user_id,
(string) $request->validated('q', ''),
),
]);
}
private function authorizeDiscordAccount(Request $request, SocialAccount $account): void
{
$workspace = $request->user()->currentWorkspace;
abort_unless(
$workspace && $account->workspace_id === $workspace->id && $account->platform === SocialPlatform::Discord,
Response::HTTP_FORBIDDEN,
);
$this->authorize('view', $workspace);
}
}