configData = $configData; $this->consumerKey = env('SSM_X_CONSUMER_KEY'); $this->consumerSecret = env('SSM_X_CONSUMER_SECRET'); $this->oauthCallbackUrl = env('DIST_SITE_URL') . '/dist/callback/twitter'; if (isset($configData['accountInfo'])) { //初始化1.1版本的配置信息 $access_token = $configData['accountInfo']['access_token']; $backup_field1 = json_decode($configData['accountInfo']['backup_field1'],true); $access_token_secret = $backup_field1['accessTokenSecret']; if (!empty($access_token) && !empty($access_token_secret)) { $this->twitterOAuth = new TwitterOAuth( $this->consumerKey, $this->consumerSecret, $access_token, $access_token_secret ); } } } public function login() { $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret); $requestToken = $connection->oauth('oauth/request_token', ['oauth_callback' => $this->oauthCallbackUrl]); session(['twitter_oauth_token' => $requestToken['oauth_token']]); session(['twitter_oauth_token_secret' => $requestToken['oauth_token_secret']]); $url = $connection->url('oauth/authorize', ['oauth_token' => $requestToken['oauth_token']]); return [ 'status' => true, 'data' => ['url' => $url] ]; } public function loginCallback(Request $request) { $oauthToken = $request->get('oauth_token'); $oauthVerifier = $request->get('oauth_verifier'); $requestToken = session('twitter_oauth_token'); $requestTokenSecret = session('twitter_oauth_token_secret'); $connection = new TwitterOAuth( $this->consumerKey, $this->consumerSecret, $requestToken, $requestTokenSecret ); $accessToken = $connection->oauth('oauth/access_token', [ 'oauth_verifier' => $oauthVerifier ]); $expiresAt = Carbon::now()->addDays(90)->format('Y-m-d H:i:s'); return [ 'status' => true, 'data' => [ 'accessToken' => $accessToken['oauth_token'], 'backupField1' => json_encode(['accessTokenSecret'=>$accessToken['oauth_token_secret']]), 'userId' => $accessToken['user_id'], 'userName' => $accessToken['screen_name'], 'accessToken_expiresAt' => $expiresAt, ] ]; } public function postImage($message, $imagePaths, $accessToken = null) { Log::info('开始发布图片帖子'); try { $this->twitterOAuth->setApiVersion('1.1'); // 强制使用 v1.1 API $this->twitterOAuth->setTimeouts(10, 30); $mediaIds = []; foreach ($imagePaths as $imagePath) { $imagePath = toStoragePath($imagePath); if (!file_exists($imagePath)) { throw new \Exception("图片不存在: {$imagePath}"); } //1.1版本上传媒体文件 $uploadedMedia = $this->twitterOAuth->upload('media/upload', [ 'media' => $imagePath ]); Log::info('1.1版本上传媒体文件完成'); if (isset($uploadedMedia->error)) { throw new \Exception('媒体上传失败: ' . json_encode($uploadedMedia)); } $mediaIds[] = $uploadedMedia->media_id_string; } // 2.0版本发布推文 $this->twitterOAuth->setApiVersion('2'); $tweet = $this->twitterOAuth->post('tweets', [ 'text' => $message, 'media' => !empty($mediaIds) ? ['media_ids' => $mediaIds] : null ]); Log::info('2.0版本发布推文完成'); if (isset($tweet->errors)) { throw new \Exception('推文发布失败: ' . json_encode($tweet->errors)); } return ['status' => true, 'data' => [ 'responseIds' => [$tweet->data->id], ]]; } catch (\Exception $e) { return ['status' => false, 'data' => $e->getMessage()]; } } public function postVideo($message, $videoPath, $accessToken = null) { try { $this->twitterOAuth->setTimeouts(10, 120); $this->twitterOAuth->setApiVersion('1.1'); $videoPath = toStoragePath($videoPath); // Verify file exists and is readable if (!file_exists($videoPath) || !is_readable($videoPath)) { throw new Exception("Video file does not exist or is not readable: $videoPath"); } // Upload video with chunked upload $uploadedMedia = $this->twitterOAuth->upload('media/upload', [ 'media' => $videoPath, 'media_category' => 'tweet_video' ],['chunkedUpload'=>true]); Log::info('1.1版本,分块上传视频'); $code = $this->twitterOAuth->getLastHttpCode(); if (isset($uploadedMedia->error) || $code != 200) { throw new \Exception('媒体上传失败: ' . json_encode($uploadedMedia)); } $mediaId = $uploadedMedia->media_id_string; $limit = 0; do { $status = $this->twitterOAuth->mediaStatus($mediaId); Log::info('检测视频上传状态'.print_r($status,true)); sleep(5); // 等待 5 秒 $limit++; } while ($status->processing_info->state !== 'succeeded' && $limit <= 3); // 最多重试 3 次 if ($status->processing_info->state === 'succeeded') { //print_r($status->processing_info->state); // 2.0版本发布推文 $this->twitterOAuth->setApiVersion('2'); $tweet = $this->twitterOAuth->post('tweets', [ 'text' => $message, 'media' => ['media_ids' => [$mediaId]] ]); Log::info('2.0版本发布推文'); if (isset($tweet->errors)) { throw new \Exception('推文发布失败: ' . json_encode($tweet->errors)); } return ['status' => true, 'data' => [ 'responseIds' => [$tweet->data->id], ] ]; } else { return ['status' => false, 'data' => '视频上传失败或处理超时。' ]; } } catch (\Exception $e) { return ['status' => false, 'data' => $e->getMessage()]; } } // 保持空实现 public function getComments($postId) {} public function replyToComment($commentId) {} public function deleteComment($commentId) {} }