google_refresh_token) { $this->error('找不到使用者,或該使用者尚未擁有 google_refresh_token!'); return; } $this->info("🔄 正在初始化 Google Client 并刷新 Token..."); // 2. 初始化 Google Client $client = new GoogleClient(); $client->setClientId(config('services.google.client_id')); $client->setClientSecret(config('services.google.client_secret')); // 用 refresh_token 去跟 Google 交換新的短期 access_token $client->refreshToken($user->google_refresh_token); $accessToken = $client->getAccessToken(); // 順手把新的 token 蓋回資料庫(防呆) $user->update([ 'google_access_token' => $accessToken['access_token'], 'google_token_expires_at' => now()->addSeconds($accessToken['expires_in']), ]); // 3. 呼叫 Gmail 服務 $gmailService = new Gmail($client); $this->info("🔍 正在透過標籤精準搜尋信用卡消費通知..."); $searchQuery = 'label:信用卡 -is:spam -is:trash'; $response = $gmailService->users_messages->listUsersMessages('me', [ 'maxResults' => 1, // 先抓一筆來看內文 'q' => $searchQuery ]); $messages = $response->getMessages(); if (empty($messages)) { $this->warn('❌ 找不到任何符合關鍵字的信件!請確認你的 Gmail 裡有這種類型的信。'); return; } $msgId = $messages[0]->getId(); $this->info("🎯 成功找到信件 ID: {$msgId},正在下載詳細內容..."); // 4. 抓取信件完整細節 $message = $gmailService->users_messages->get('me', $msgId, ['format' => 'full']); $payload = $message->getPayload(); $headers = $payload->getHeaders(); // 解析主旨 $subject = ''; foreach ($headers as $header) { if ($header->getName() === 'Subject') { $subject = $header->getValue(); break; } } // 解析內文 (處理可能存在的多重 Multipart 結構) $body = $this->getDecodedBody($payload); // 5. 🟢 核心現形:把內容原封不動地噴進 Log Log::info("=== 【GMAIL 測試抓取成功】 ==="); Log::info("主旨: " . $subject); Log::info("信件 ID: " . $msgId); Log::info("=== 原始內文開始 ==="); Log::info($body); Log::info("=== 原始內文結束 ==="); $this->info("✅ 抓取完畢!真實內容已成功寫入 storage/logs/laravel.log,快去打開瞧瞧吧!"); } /** * 輔助方法:遞迴解析 Gmail 的 Body 內容 */ private function getDecodedBody($part) { $body = $part->getBody(); if ($body && $body->getData()) { // Gmail 的 base64 比較特殊,需要把 - 和 _ 換回來 $sanitizedData = str_replace(['-', '_'], ['+', '/'], $body->getData()); return base64_decode($sanitizedData); } $parts = $part->getParts(); if ($parts) { foreach ($parts as $subPart) { $result = $this->getDecodedBody($subPart); if ($result) return $result; } } return ''; } }