Browse Source

社媒对接

moshaorui 1 week ago
parent
commit
c0b70393a2

+ 9 - 11
app/Console/Commands/TimerSsmPost.php

@@ -72,7 +72,6 @@ class TimerSsmPost extends Command
             $post = SmmPost::getPostById($log->post_id);
             $post = SmmPost::getPostById($log->post_id);
             $message = $post->message;
             $message = $post->message;
             $imageVideoUrl = $post->image_video_url;
             $imageVideoUrl = $post->image_video_url;
-           // $imageVideoUrl = storage_path($imageVideoUrl);
             $mediaName = $log->media_name;
             $mediaName = $log->media_name;
             $postType = $post->post_type;
             $postType = $post->post_type;
             $accountInfo = SmmUserAccount::getAccountById($log->account_id);
             $accountInfo = SmmUserAccount::getAccountById($log->account_id);
@@ -89,22 +88,21 @@ class TimerSsmPost extends Command
                 $response = $ssmService->postImage($message, $imageVideoUrl,$accessToken);
                 $response = $ssmService->postImage($message, $imageVideoUrl,$accessToken);
             } else {
             } else {
                 $imageVideoUrl = CommonHelper::ossUrl($imageVideoUrl);
                 $imageVideoUrl = CommonHelper::ossUrl($imageVideoUrl);
-               // $response = $ssmService->postVideo($message, $imageVideoUrl,$accessToken);
+                $response = $ssmService->postVideo($message, $imageVideoUrl,$accessToken);
+
+//                $response = $ssmService->postVideo([
+//                    'title' => 'Test Video',
+//                    'description' => 'This is a test video111',
+//                    'categoryId' => '22', // People & Blogs
+//                    'privacyStatus' => 'private'
+//                ], '/mnt/hgfs/wwwroot/mtb_dcatadmin_plus/1.mp4', $accessToken);
 
 
-                $response = $ssmService->postVideo([
-                    'title' => 'Test Video',
-                    'description' => 'This is a test video111',
-                    'categoryId' => '22', // People & Blogs
-                    'privacyStatus' => 'private'
-                ], '/mnt/hgfs/wwwroot/mtb_dcatadmin_plus/1.mp4', $accessToken);
             }
             }
-            dd($response);
+
             //更新post_logs表
             //更新post_logs表
             if ($response['status'] == true) {
             if ($response['status'] == true) {
                 $log->status = 1;
                 $log->status = 1;
                 $log->response_ids = json_encode($response['data']['responseIds']);
                 $log->response_ids = json_encode($response['data']['responseIds']);
-                $log->request_content = json_encode($response['data']['requestContent']);
-                $log->response_content = json_encode($response['data']['responseContent']);
                 $log->updated_at = Carbon::now();
                 $log->updated_at = Carbon::now();
                 $log->send_time = Carbon::now();
                 $log->send_time = Carbon::now();
                 $log->save();
                 $log->save();

+ 7 - 3
app/Distributor/Controllers/SmmPostController.php

@@ -29,7 +29,7 @@ class SmmPostController extends AdminDistController
     {
     {
 //        $ssmService = new SmmService('youtube');
 //        $ssmService = new SmmService('youtube');
 //        $videoCategories = $ssmService->getVideoCategories();
 //        $videoCategories = $ssmService->getVideoCategories();
-//        var_dump($videoCategories);
+//        var_dump($videoCategories['data']);
 //        exit;
 //        exit;
         return $content
         return $content
             ->header('发报帖子')
             ->header('发报帖子')
@@ -118,7 +118,6 @@ class SmmPostController extends AdminDistController
                 //转换时间格式
                 //转换时间格式
                 $send_time = Carbon::createFromFormat('Y-m-d H:i:s', $post['send_time']);
                 $send_time = Carbon::createFromFormat('Y-m-d H:i:s', $post['send_time']);
             }
             }
-            //dd($post);
             //保存数据
             //保存数据
             SmmPost::create($post,$send_time,$image_video_url);
             SmmPost::create($post,$send_time,$image_video_url);
             //最后一步
             //最后一步
@@ -135,11 +134,16 @@ class SmmPostController extends AdminDistController
      * 上传图片到本地
      * 上传图片到本地
      */
      */
     public function upload() {
     public function upload() {
-        $disk = $this->disk('oss');
         // 获取上传的文件
         // 获取上传的文件
         $file = $this->file();
         $file = $this->file();
         $dir = 'ssm/'.getDistributorId();
         $dir = 'ssm/'.getDistributorId();
         $newName = md5(uniqid() . mt_rand()) .'.'.$file->getClientOriginalExtension();
         $newName = md5(uniqid() . mt_rand()) .'.'.$file->getClientOriginalExtension();
+        //保存到本地
+        $disk = $this->disk('local');
+        $result = $disk->putFileAs($dir, $file, $newName);
+
+        //oss 保存
+        $disk = $this->disk('oss');
         $result = $disk->putFileAs($dir, $file, $newName);
         $result = $disk->putFileAs($dir, $file, $newName);
         return $result
         return $result
             ? $this->responseUploaded($result, $disk->url($result))
             ? $this->responseUploaded($result, $disk->url($result))

+ 2 - 1
app/Distributor/Controllers/SmmUserAccountController.php

@@ -116,7 +116,8 @@ class SmmUserAccountController extends AdminDistController
                 $userName = $result['data']['userName'];
                 $userName = $result['data']['userName'];
                 $userId = $result['data']['userId'];
                 $userId = $result['data']['userId'];
                 $refreshToken = isset($result['data']['refreshToken']) ? $result['data']['refreshToken'] : '';
                 $refreshToken = isset($result['data']['refreshToken']) ? $result['data']['refreshToken'] : '';
-                SmmUserAccount::createAccountIfMediaExists($mediaName, $userId,$userName, $accessToken,$expiresAt,$refreshToken);
+                $backupField1 = isset($result['data']['backupField1']) ? $result['data']['backupField1'] : '';
+                SmmUserAccount::createAccountIfMediaExists($mediaName, $userId,$userName, $accessToken,$expiresAt,$refreshToken,$backupField1);
                 return response()->json(['code' => 1, 'msg' => 'success']);
                 return response()->json(['code' => 1, 'msg' => 'success']);
             } else {
             } else {
                 return response()->json(['code' => 0,'msg' => $result['data']]);
                 return response()->json(['code' => 0,'msg' => $result['data']]);

+ 0 - 30
app/Services/Smm/FacebookService.php

@@ -113,8 +113,6 @@ class FacebookService implements SmmPlatformInterface
                 return $pagesInfo;
                 return $pagesInfo;
             }
             }
             $results = [];
             $results = [];
-            $requestContent = [];
-            $responseContent = [];
             foreach ($pagesInfo['data'] as $page) {
             foreach ($pagesInfo['data'] as $page) {
                 $mediaIds = [];
                 $mediaIds = [];
                 foreach ($imagePaths as $imagePath) {
                 foreach ($imagePaths as $imagePath) {
@@ -124,17 +122,10 @@ class FacebookService implements SmmPlatformInterface
                         ->post("https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/photos", [
                         ->post("https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/photos", [
                             'published' => false,
                             'published' => false,
                         ]);
                         ]);
-
                     if ($uploadResponse->failed()) {
                     if ($uploadResponse->failed()) {
                         return ['status' => false, 'data' => $uploadResponse->json('error.message')];
                         return ['status' => false, 'data' => $uploadResponse->json('error.message')];
                     }
                     }
                     $mediaIds[] = ['media_fbid' => $uploadResponse->json('id')];
                     $mediaIds[] = ['media_fbid' => $uploadResponse->json('id')];
-                    $requestContent[] = [
-                        'url'=> "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/photos",
-                        'source' => $imagePath,
-                        'published' => false,
-                    ];
-                    $responseContent[] = $uploadResponse->json();
                 }
                 }
 
 
                 // 发布帖子
                 // 发布帖子
@@ -149,19 +140,11 @@ class FacebookService implements SmmPlatformInterface
                 }
                 }
 
 
                 $results[] = $postResponse->json('id');
                 $results[] = $postResponse->json('id');
-                $requestContent[] = [
-                    'url' => "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/feed",
-                   'message' => $message,
-                    'attached_media' => json_encode($mediaIds),
-                ];
-                $responseContent[] = $postResponse->json();
             }
             }
 
 
             return ['status' => true,
             return ['status' => true,
                 'data' => [
                 'data' => [
                     'responseIds' => $results,
                     'responseIds' => $results,
-                    'requestContent' => $requestContent,
-                    'responseContent' => $responseContent,
                 ]];
                 ]];
 
 
         } catch (\Exception $e) {
         } catch (\Exception $e) {
@@ -176,10 +159,7 @@ class FacebookService implements SmmPlatformInterface
             if (!$pagesInfo['status']) {
             if (!$pagesInfo['status']) {
                 return $pagesInfo;
                 return $pagesInfo;
             }
             }
-
             $results = [];
             $results = [];
-            $requestContent = [];
-            $responseContent = [];
             foreach ($pagesInfo['data'] as $page) {
             foreach ($pagesInfo['data'] as $page) {
                 $response = Http::withToken($page['pageAccessToken'])
                 $response = Http::withToken($page['pageAccessToken'])
                     ->attach('source', file_get_contents($videoPath), basename($videoPath))
                     ->attach('source', file_get_contents($videoPath), basename($videoPath))
@@ -190,21 +170,11 @@ class FacebookService implements SmmPlatformInterface
                 if ($response->failed()) {
                 if ($response->failed()) {
                     throw new \Exception($response->json('error.message'));
                     throw new \Exception($response->json('error.message'));
                 }
                 }
-
                 $results[] = $response->json('id');
                 $results[] = $response->json('id');
-                $requestContent[] = [
-                    'url' => "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/videos",
-                    'source' => $videoPath,
-                    'description' => $message,
-                ];
-                $responseContent[] = $response->json();
             }
             }
-
             return ['status' => true,
             return ['status' => true,
                 'data' => [
                 'data' => [
                     'responseIds' => $results,
                     'responseIds' => $results,
-                    'requestContent' => $requestContent,
-                    'responseContent' => $responseContent,
                 ]];
                 ]];
 
 
         } catch (\Exception $e) {
         } catch (\Exception $e) {

+ 0 - 20
app/Services/Smm/InstagramService.php

@@ -142,14 +142,9 @@ class InstagramService implements SmmPlatformInterface
                     return ['status' => false, 'data' => '错误:无法发布图片 - ' . json_encode($publishData)];
                     return ['status' => false, 'data' => '错误:无法发布图片 - ' . json_encode($publishData)];
                 }
                 }
                 $postIds = [$publishData['id']];
                 $postIds = [$publishData['id']];
-                $requestContent = [$postData];
-                $responseContent = [$publishData];
-
                 return ['status' => true,
                 return ['status' => true,
                     'data' => [
                     'data' => [
                         'responseIds' => $postIds,
                         'responseIds' => $postIds,
-                        'requestContent' => $requestContent,
-                        'responseContent' => $responseContent,
                     ]];
                     ]];
             }
             }
 
 
@@ -190,7 +185,6 @@ class InstagramService implements SmmPlatformInterface
             $carouselResponse = Http::asForm()->post("https://graph.instagram.com/v21.0/{$igUserId}/media",$postData );
             $carouselResponse = Http::asForm()->post("https://graph.instagram.com/v21.0/{$igUserId}/media",$postData );
 
 
             $carouselData = $carouselResponse->json();
             $carouselData = $carouselResponse->json();
-            $responseContent[] = $carouselData;
             if (!isset($carouselData['id'])) {
             if (!isset($carouselData['id'])) {
                 return ['status' => false, 'data' => '错误:无法创建轮播容器 - ' . json_encode($carouselData)];
                 return ['status' => false, 'data' => '错误:无法创建轮播容器 - ' . json_encode($carouselData)];
             }
             }
@@ -206,7 +200,6 @@ class InstagramService implements SmmPlatformInterface
             $publishResponse = Http::post("https://graph.instagram.com/v21.0/{$igUserId}/media_publish", $postData);
             $publishResponse = Http::post("https://graph.instagram.com/v21.0/{$igUserId}/media_publish", $postData);
 
 
             $publishData = $publishResponse->json();
             $publishData = $publishResponse->json();
-            $responseContent[] = $publishData;
             if (!isset($publishData['id'])) {
             if (!isset($publishData['id'])) {
                 return ['status' => false, 'data' => '错误:无法发布轮播 - ' . json_encode($publishData)];
                 return ['status' => false, 'data' => '错误:无法发布轮播 - ' . json_encode($publishData)];
             }
             }
@@ -214,8 +207,6 @@ class InstagramService implements SmmPlatformInterface
             return ['status' => true,
             return ['status' => true,
                 'data' => [
                 'data' => [
                     'responseIds' => $postIds,
                     'responseIds' => $postIds,
-                    'requestContent' => $requestContent,
-                    'responseContent' => $responseContent,
                 ]];
                 ]];
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             //Log::error('Instagram postImage error: ' . $e->getMessage());
             //Log::error('Instagram postImage error: ' . $e->getMessage());
@@ -234,9 +225,6 @@ class InstagramService implements SmmPlatformInterface
             }
             }
             $igUserId = $this->configData['accountInfo']['account_id'];
             $igUserId = $this->configData['accountInfo']['account_id'];
 
 
-            $requestContent = [];
-            $responseContent = [];
-
             // Create media container for video
             // Create media container for video
             $postData = [
             $postData = [
                 'media_type' => 'REELS',
                 'media_type' => 'REELS',
@@ -244,13 +232,11 @@ class InstagramService implements SmmPlatformInterface
                 'caption' => $message,
                 'caption' => $message,
                 'access_token' => $accessToken,
                 'access_token' => $accessToken,
             ];
             ];
-            $requestContent[] = ['url'=>"https://graph.instagram.com/v21.0/{$igUserId}/media", 'postData' => $postData];
             $containerResponse = Http::asForm()->post("https://graph.instagram.com/v21.0/{$igUserId}/media", $postData);
             $containerResponse = Http::asForm()->post("https://graph.instagram.com/v21.0/{$igUserId}/media", $postData);
             $containerData = $containerResponse->json();
             $containerData = $containerResponse->json();
             if (!isset($containerData['id'])) {
             if (!isset($containerData['id'])) {
                 return ['status' => false, 'data' => '错误:无法创建视频容器 - ' . json_encode($containerData)];
                 return ['status' => false, 'data' => '错误:无法创建视频容器 - ' . json_encode($containerData)];
             }
             }
-            $responseContent[] = $containerData;
             // Check container status (video processing may take time)
             // Check container status (video processing may take time)
             $status = 'IN_PROGRESS';
             $status = 'IN_PROGRESS';
             $maxAttempts = 10;
             $maxAttempts = 10;
@@ -261,7 +247,6 @@ class InstagramService implements SmmPlatformInterface
                     'fields' => 'status',
                     'fields' => 'status',
                     'access_token' => $accessToken,
                     'access_token' => $accessToken,
                 ];
                 ];
-                $requestContent[] = ['url'=>"https://graph.instagram.com/{$containerData['id']}", 'postData' => $postData];
                 $statusResponse = Http::get("https://graph.instagram.com/{$containerData['id']}", $postData);
                 $statusResponse = Http::get("https://graph.instagram.com/{$containerData['id']}", $postData);
 
 
                 $statusData = $statusResponse->json();
                 $statusData = $statusResponse->json();
@@ -271,7 +256,6 @@ class InstagramService implements SmmPlatformInterface
                 if ($status === 'ERROR') {
                 if ($status === 'ERROR') {
                     return ['status' => false, 'data' => '错误:视频处理失败'];
                     return ['status' => false, 'data' => '错误:视频处理失败'];
                 }
                 }
-                $responseContent[] = $statusData;
             }
             }
             if ($status !== 'FINISHED') {
             if ($status !== 'FINISHED') {
                 return ['status' => false, 'data' => '错误:视频处理超时'];
                 return ['status' => false, 'data' => '错误:视频处理超时'];
@@ -281,20 +265,16 @@ class InstagramService implements SmmPlatformInterface
                 'creation_id' => $containerData['id'],
                 'creation_id' => $containerData['id'],
                 'access_token' => $accessToken,
                 'access_token' => $accessToken,
             ];
             ];
-            $requestContent[] = ['url'=>"https://graph.instagram.com/v21.0/{$igUserId}/media_publish", 'postData' => $postData];
             $publishResponse = Http::post("https://graph.instagram.com/v21.0/{$igUserId}/media_publish",$postData);
             $publishResponse = Http::post("https://graph.instagram.com/v21.0/{$igUserId}/media_publish",$postData);
 
 
             $publishData = $publishResponse->json();
             $publishData = $publishResponse->json();
             if (!isset($publishData['id'])) {
             if (!isset($publishData['id'])) {
                 return ['status' => false, 'data' => '错误:无法发布视频 - ' . json_encode($publishData)];
                 return ['status' => false, 'data' => '错误:无法发布视频 - ' . json_encode($publishData)];
             }
             }
-            $responseContent[] = $publishData;
             $postIds = [$publishData['id']];
             $postIds = [$publishData['id']];
             return ['status' => true,
             return ['status' => true,
                 'data' => [
                 'data' => [
                     'responseIds' => $postIds,
                     'responseIds' => $postIds,
-                    'requestContent' => $requestContent,
-                    'responseContent' => $responseContent,
                 ]];
                 ]];
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             return ['status' => false, 'data' => '错误:' . $e->getMessage()];
             return ['status' => false, 'data' => '错误:' . $e->getMessage()];

+ 130 - 41
app/Services/Smm/TwitterService.php

@@ -5,6 +5,7 @@ namespace App\Services\Smm;
 use App\Services\Contracts\SmmPlatformInterface;
 use App\Services\Contracts\SmmPlatformInterface;
 use Abraham\TwitterOAuth\TwitterOAuth;
 use Abraham\TwitterOAuth\TwitterOAuth;
 use Carbon\Carbon;
 use Carbon\Carbon;
+use CURLFile;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 
 
 class TwitterService implements SmmPlatformInterface
 class TwitterService implements SmmPlatformInterface
@@ -21,17 +22,24 @@ class TwitterService implements SmmPlatformInterface
     {
     {
         $this->configData = $configData;
         $this->configData = $configData;
 
 
-        $this->consumerKey = env('TWITTER_CONSUMER_KEY');
-        $this->consumerSecret = env('TWITTER_CONSUMER_SECRET');
+        $this->consumerKey = 'ahCsl1rD6ml2Ofma5UKUaMveJ';
+        $this->consumerSecret = 'CsS5AHR1vIVuuhWHrbV8RR9DrF6JvZjjMOIt7oA9YubpxllDWD';
         $this->oauthCallbackUrl = env('DIST_SITE_URL') . '/dist/callback/twitter';
         $this->oauthCallbackUrl = env('DIST_SITE_URL') . '/dist/callback/twitter';
 
 
-        if (!empty($configData['access_token']) && !empty($configData['access_token_secret'])) {
-            $this->twitterOAuth = new TwitterOAuth(
-                $this->consumerKey,
-                $this->consumerSecret,
-                $configData['access_token'],
-                $configData['access_token_secret']
-            );
+        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
+                );
+
+            }
         }
         }
     }
     }
 
 
@@ -65,94 +73,174 @@ class TwitterService implements SmmPlatformInterface
             $requestToken,
             $requestToken,
             $requestTokenSecret
             $requestTokenSecret
         );
         );
-
         $accessToken = $connection->oauth('oauth/access_token', [
         $accessToken = $connection->oauth('oauth/access_token', [
             'oauth_verifier' => $oauthVerifier
             'oauth_verifier' => $oauthVerifier
         ]);
         ]);
-
+        $expiresAt = Carbon::now()->addDays(90)->format('Y-m-d H:i:s');
         return [
         return [
             'status' => true,
             'status' => true,
             'data' => [
             'data' => [
                 'accessToken' => $accessToken['oauth_token'],
                 'accessToken' => $accessToken['oauth_token'],
-                'accessTokenSecret' => $accessToken['oauth_token_secret'],
+                'backupField1' => json_encode(['accessTokenSecret'=>$accessToken['oauth_token_secret']]),
                 'userId' => $accessToken['user_id'],
                 'userId' => $accessToken['user_id'],
                 'userName' => $accessToken['screen_name'],
                 'userName' => $accessToken['screen_name'],
+                'accessToken_expiresAt' => $expiresAt,
             ]
             ]
         ];
         ];
     }
     }
 
 
+
     public function postImage($message, $imagePaths, $accessToken = null)
     public function postImage($message, $imagePaths, $accessToken = null)
     {
     {
         try {
         try {
+            $this->twitterOAuth->setApiVersion('1.1'); // 强制使用 v1.1 API
+            $this->twitterOAuth->setTimeouts(10, 30);
             $mediaIds = [];
             $mediaIds = [];
-
             foreach ($imagePaths as $imagePath) {
             foreach ($imagePaths as $imagePath) {
+                $imagePath = toStoragePath($imagePath);
                 if (!file_exists($imagePath)) {
                 if (!file_exists($imagePath)) {
                     throw new \Exception("图片不存在: {$imagePath}");
                     throw new \Exception("图片不存在: {$imagePath}");
                 }
                 }
-
+                //1.1版本上传媒体文件
                 $uploadedMedia = $this->twitterOAuth->upload('media/upload', [
                 $uploadedMedia = $this->twitterOAuth->upload('media/upload', [
                     'media' => $imagePath
                     'media' => $imagePath
                 ]);
                 ]);
-
                 if (isset($uploadedMedia->error)) {
                 if (isset($uploadedMedia->error)) {
                     throw new \Exception('媒体上传失败: ' . json_encode($uploadedMedia));
                     throw new \Exception('媒体上传失败: ' . json_encode($uploadedMedia));
                 }
                 }
-
                 $mediaIds[] = $uploadedMedia->media_id_string;
                 $mediaIds[] = $uploadedMedia->media_id_string;
             }
             }
-
-            $tweet = $this->twitterOAuth->post('statuses/update', [
-                'status' => $message,
-                'media_ids' => implode(',', $mediaIds)
+            // 2.0版本发布推文
+            $this->twitterOAuth->setApiVersion('2');
+            $tweet = $this->twitterOAuth->post('tweets', [
+                'text' => $message,
+                'media' => !empty($mediaIds) ? ['media_ids' => $mediaIds] : null
             ]);
             ]);
-
             if (isset($tweet->errors)) {
             if (isset($tweet->errors)) {
                 throw new \Exception('推文发布失败: ' . json_encode($tweet->errors));
                 throw new \Exception('推文发布失败: ' . json_encode($tweet->errors));
             }
             }
-
-            return [
-                'status' => true,
+            return ['status' => true,
                 'data' => [
                 'data' => [
-                    'tweetId' => $tweet->id_str,
-                    'mediaIds' => $mediaIds
-                ]
-            ];
+                    'responseIds' => [$tweet->data->id],
+                ]];
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             return ['status' => false, 'data' => $e->getMessage()];
             return ['status' => false, 'data' => $e->getMessage()];
         }
         }
     }
     }
 
 
+
     public function postVideo($message, $videoPath, $accessToken = null)
     public function postVideo($message, $videoPath, $accessToken = null)
+
     {
     {
+
         try {
         try {
+
+            $this->twitterOAuth->setTimeouts(10, 30);
+
+            //转为本地路径
+
+            $videoPath = toStoragePath($videoPath);
+
             if (!file_exists($videoPath)) {
             if (!file_exists($videoPath)) {
+
                 throw new \Exception("视频文件不存在: {$videoPath}");
                 throw new \Exception("视频文件不存在: {$videoPath}");
+
             }
             }
 
 
-            $uploadedVideo = $this->twitterOAuth->upload('media/upload', [
-                'media' => $videoPath,
-                'media_type' => 'video/mp4'
-            ], true); // 注意: chunked upload
+            // 获取视频 MIME 类型
+            $mimeType = mime_content_type($videoPath);
+            if (strpos($mimeType, 'video/') !== 0) {
+
+                throw new \Exception("文件不是有效的视频文件。MIME 类型: " . $mimeType);
 
 
-            if (isset($uploadedVideo->error)) {
-                throw new \Exception('视频上传失败: ' . json_encode($uploadedVideo));
             }
             }
 
 
-            $tweet = $this->twitterOAuth->post('statuses/update', [
-                'status' => $message,
-                'media_ids' => $uploadedVideo->media_id_string
+            // 获取视频文件大小 (以字节为单位)
+            $fileSize = filesize($videoPath);
+
+            // 1.1版本上传媒体文件 - 初始化上传
+            $initParams = [
+                'command' => 'INIT',
+                'media_type' => $mimeType,
+                'media_category' => 'tweet_video',
+                'total_bytes' => $fileSize,
+            ];
+            $uploadedInit = $this->twitterOAuth->upload('media/upload', $initParams, true);
+
+            if (!isset($uploadedInit->media_id_string)) {
+
+                throw new \Exception('视频上传初始化失败: ' . json_encode($uploadedInit));
+
+            }
+            $mediaId = $uploadedInit->media_id_string;
+
+            // 分段上传视频
+            $fileHandle = fopen($videoPath, 'r');
+            $segmentId = 0;
+            while (!feof($fileHandle)) {
+                $chunk = fread($fileHandle, 1024 * 1024); // 每次上传 1MB
+                $segmentParams = [
+                    'command' => 'APPEND',
+                    'media_id' => $mediaId,
+                    'segment_index' => $segmentId,
+                    'media' => $chunk,
+                ];
+                $uploadedSegment = $this->twitterOAuth->upload('media/upload', $segmentParams, true);
+                if (!empty($uploadedSegment->error)) {
+                    fclose($fileHandle);
+                    throw new \Exception('视频分段上传失败: ' . json_encode($uploadedSegment));
+                }
+                $segmentId++;
+            }
+            fclose($fileHandle);
+
+            // 1.1版本上传媒体文件 - 完成上传
+            $finalizeParams = [
+                'command' => 'FINALIZE',
+                'media_id' => $mediaId,
+            ];
+            $uploadedFinalize = $this->twitterOAuth->upload('media/upload', $finalizeParams, true);
+            if (isset($uploadedFinalize->processing_info)) {
+                $processingInfo = $uploadedFinalize->processing_info;
+                $checkStatusSecs = $processingInfo->check_after_secs;
+                $status = $processingInfo->state;
+
+                while ($status !== 'succeeded' && $status !== 'failed') {
+                    sleep($checkStatusSecs);
+                    $statusParams = [
+                        'command' => 'STATUS',
+                        'media_id' => $mediaId,
+                    ];
+                    $uploadedStatus = $this->twitterOAuth->upload('media/upload', $statusParams, true);
+                    if (!empty($uploadedStatus->error)) {
+                        throw new \Exception('获取视频上传状态失败: ' . json_encode($uploadedStatus));
+                    }
+                    $processingInfo = $uploadedStatus->processing_info;
+                    $checkStatusSecs = $processingInfo->check_after_secs;
+                    $status = $processingInfo->state;
+                    if ($status === 'failed') {
+                        throw new \Exception('视频上传失败,Twitter 处理失败: ' . json_encode($uploadedStatus));
+                    }
+                }
+            } elseif (isset($uploadedFinalize->error)) {
+                throw new \Exception('视频上传完成步骤失败: ' . json_encode($uploadedFinalize));
+            }
+
+
+            // 2.0版本发布推文
+            $this->twitterOAuth->setApiVersion('2');
+            $tweet = $this->twitterOAuth->post('tweets', [
+                'text' => $message,
+                'media' => ['media_ids' => [$mediaId]],
             ]);
             ]);
 
 
             if (isset($tweet->errors)) {
             if (isset($tweet->errors)) {
                 throw new \Exception('推文发布失败: ' . json_encode($tweet->errors));
                 throw new \Exception('推文发布失败: ' . json_encode($tweet->errors));
             }
             }
 
 
-            return [
-                'status' => true,
+            return ['status' => true,
                 'data' => [
                 'data' => [
-                    'tweetId' => $tweet->id_str,
-                    'mediaId' => $uploadedVideo->media_id_string
+                    'responseIds' => [$tweet->data->id],
                 ]
                 ]
             ];
             ];
         } catch (\Exception $e) {
         } catch (\Exception $e) {
@@ -160,6 +248,7 @@ class TwitterService implements SmmPlatformInterface
         }
         }
     }
     }
 
 
+
     // 保持空实现
     // 保持空实现
     public function getComments($postId) {}
     public function getComments($postId) {}
     public function replyToComment($commentId) {}
     public function replyToComment($commentId) {}

+ 0 - 5
app/Services/Smm/YoutubeService.php

@@ -93,9 +93,7 @@ class YoutubeService implements SmmPlatformInterface
         // 初始化 YouTube 服务
         // 初始化 YouTube 服务
         $youtube = new Google_Service_YouTube($this->client);
         $youtube = new Google_Service_YouTube($this->client);
         try {
         try {
-            $requestContent = [];
             // 定义视频元数据
             // 定义视频元数据
-            $requestContent[] = $snippetData;
             $snippetData = [
             $snippetData = [
                 'title' => $message,
                 'title' => $message,
                 'description' => '',
                 'description' => '',
@@ -135,12 +133,9 @@ class YoutubeService implements SmmPlatformInterface
             fclose($handle);
             fclose($handle);
             $this->client->setDefer(false);
             $this->client->setDefer(false);
             //echo "Video uploaded: " . $status['id'];
             //echo "Video uploaded: " . $status['id'];
-            $responseContent = [$status];
             return ['status' => true,
             return ['status' => true,
                 'data' => [
                 'data' => [
                     'responseIds' => [$status['id']],
                     'responseIds' => [$status['id']],
-                    'requestContent' => $requestContent,
-                    'responseContent' => $responseContent,
                 ]];
                 ]];
         } catch (Google_Service_Exception $e) {
         } catch (Google_Service_Exception $e) {
             return ['status' => false, 'data' => $e->getMessage()];
             return ['status' => false, 'data' => $e->getMessage()];

+ 12 - 0
app/helpers.php

@@ -327,3 +327,15 @@ if (!function_exists('truncateString')) {
         return $string;
         return $string;
     }
     }
 }
 }
+
+
+/*
+ * 带http的url转化为storage_path路径
+ */
+if (!function_exists('toStoragePath')) {
+    function toStoragePath($httpUrl) {
+        $parsed = parse_url($httpUrl);
+        $path =  ltrim($parsed['path'], '/');
+        return storage_path('app/'.$path);
+    }
+}