123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- <?php
- namespace App\Services\Smm;
- use App\Services\Contracts\SmmPlatformInterface;
- use Abraham\TwitterOAuth\TwitterOAuth;
- use Carbon\Carbon;
- use CURLFile;
- use Illuminate\Http\Request;
- class TwitterService implements SmmPlatformInterface
- {
- protected $configData;
- protected $twitterOAuth;
- protected $consumerKey;
- protected $consumerSecret;
- protected $oauthCallbackUrl;
- public function __construct($configData)
- {
- $this->configData = $configData;
- $this->consumerKey = 'ahCsl1rD6ml2Ofma5UKUaMveJ';
- $this->consumerSecret = 'CsS5AHR1vIVuuhWHrbV8RR9DrF6JvZjjMOIt7oA9YubpxllDWD';
- $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)
- {
- 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
- ]);
- 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
- ]);
- 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, 30);
- //转为本地路径
- $videoPath = toStoragePath($videoPath);
- if (!file_exists($videoPath)) {
- throw new \Exception("视频文件不存在: {$videoPath}");
- }
- // 获取视频 MIME 类型
- $mimeType = mime_content_type($videoPath);
- if (strpos($mimeType, 'video/') !== 0) {
- throw new \Exception("文件不是有效的视频文件。MIME 类型: " . $mimeType);
- }
- // 获取视频文件大小 (以字节为单位)
- $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)) {
- 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 getComments($postId) {}
- public function replyToComment($commentId) {}
- public function deleteComment($commentId) {}
- }
|