` * message/channel_post: the signed code carries the workspace, so we link the * originating channel to it. Everything else is acknowledged and ignored. */ public function handle(Request $request): Response { $secret = (string) config('trypost.platforms.telegram.webhook_secret'); abort_if( $secret === '' || ! hash_equals($secret, (string) $request->header('X-Telegram-Bot-Api-Secret-Token')), SymfonyResponse::HTTP_FORBIDDEN, ); $update = $request->all(); $chat = data_get($update, 'message.chat') ?? data_get($update, 'channel_post.chat'); $text = data_get($update, 'message.text') ?? data_get($update, 'channel_post.text'); if (! is_array($chat) || ! is_string($text) || ! preg_match('/^\/connect(?:@\S+)?\s+(\S+)/', $text, $matches)) { return response()->noContent(); } $payload = TelegramConnectCode::decode($matches[1]); $workspace = $payload === null ? null : Workspace::find(data_get($payload, 'workspace_id')); if ($workspace === null) { return response()->noContent(); } $chatId = (string) data_get($chat, 'id'); $username = data_get($chat, 'username'); // Mirror the controller's limit gate: block only brand-new accounts, never reconnects. $isNewAccount = ! $workspace->socialAccounts() ->where('platform', SocialPlatform::Telegram->value) ->where('platform_user_id', $chatId) ->exists(); if ($isNewAccount && $this->workspaceAtAccountLimit($workspace)) { return response()->noContent(); } // Consume the code once so a leaked code can't be replayed to link another chat. if (! Cache::add("telegram:connect:{$payload['nonce']}", true, now()->addMinutes(15))) { return response()->noContent(); } $workspace->socialAccounts()->updateOrCreate( [ 'platform' => SocialPlatform::Telegram->value, 'platform_user_id' => $chatId, ], [ 'username' => $username, 'display_name' => data_get($chat, 'title') ?? $username ?? "Telegram {$chatId}", 'access_token' => '', 'refresh_token' => '', 'token_expires_at' => null, 'scopes' => [], 'status' => Status::Connected, 'error_message' => null, 'disconnected_at' => null, 'meta' => [ 'chat_id' => $chatId, 'username' => $username, 'type' => data_get($chat, 'type'), 'connect_nonce' => data_get($payload, 'nonce'), ], ], ); return response()->noContent(); } private function workspaceAtAccountLimit(Workspace $workspace): bool { if (config('trypost.self_hosted')) { return false; } $limit = Feature::for($workspace->account)->value(SocialAccountLimit::class); return $workspace->socialAccounts()->count() >= $limit; } }