Skip to content

Commit

Permalink
Merge pull request #91 from shutupflanders/with-videos
Browse files Browse the repository at this point in the history
Added `withVideo` support to `TwitterStatusUpdate`
  • Loading branch information
christophrumpel committed Mar 13, 2023
2 parents 1ad03a1 + 7ec3a8b commit 70c6553
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 4 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ PS: v.7.0.0 only supports Laravel 10 and PHP 8.1. If you have an older Laravel a
- [Usage](#usage)
- [Publish a Twitter status update](#publish-a-twitter-status-update)
- [Publish Twitter status update with images](#publish-twitter-status-update-with-images)
- [Publish Twitter status update with videos](#publish-twitter-status-update-with-videos)
- [Publish Twitter status update with both images and videos](#publish-twitter-status-update-with-both-images-and-videos)
- [Send a direct message](#send-a-direct-message)
- [Handle multiple Twitter Accounts](#handle-multiple-twitter-accounts)
- [Changelog](#changelog)
Expand Down Expand Up @@ -120,6 +122,32 @@ return (new TwitterStatusUpdate('Laravel notifications are awesome!'))->withImag
public_path('mohamed.png')
]);
````
### Publish Twitter status update with videos
It is possible to publish videos with your status update too. You have to pass the video path to the `withVideo` method.
````php
public function toTwitter(mixed $notifiable): TwitterMessage
{
return (new TwitterStatusUpdate('Laravel notifications are awesome!'))->withVideo('video.mp4');
}
````
If you want to use multiple videos, just pass an array of paths.
````php
return (new TwitterStatusUpdate('Laravel notifications are awesome!'))->withVideo([
public_path('video1.mp4'),
public_path('video.gif')
]);
````
### Publish Twitter status update with both images and videos
It is also possible to publish both images and videos with your status by using a mixture of the two methods.
````php
return (new TwitterStatusUpdate('Laravel notifications are awesome!'))->withVideo([
public_path('video1.mp4'),
public_path('video.gif')
])->withImage([
public_path('marcel.png'),
public_path('mohamed.png')
]);
````
### Publish Twitter status update in reply to another tweet
Additionally, you can publish a status update in reply to another tweet. That is possible using the method `inReplyTo`.
````php
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"abraham/twitteroauth": "^5.0",
"illuminate/notifications": "^10.0",
"illuminate/support": "^10.0",
"kylewm/brevity": "^0.2"
"kylewm/brevity": "^0.2",
"ext-fileinfo": "*"
},
"require-dev": {
"mockery/mockery": "^1.3.1",
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptions/CouldNotSendNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@ public static function statusUpdateTooLong(int $exceededLength): CouldNotSendNot
"Couldn't post notification, because the status message was too long by ${exceededLength} character(s)."
);
}

public static function videoCouldNotBeProcessed(string $message): CouldNotSendNotification
{
return new static(
"Couldn't post notification, Video upload failed: ".$message
);
}
}
40 changes: 40 additions & 0 deletions src/TwitterChannel.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public function send($notifiable, Notification $notification): array|object

$twitterMessage = $notification->toTwitter($notifiable);
$twitterMessage = $this->addImagesIfGiven($twitterMessage);
$twitterMessage = $this->addVideosIfGiven($twitterMessage);

$twitterApiResponse = $this->twitter->post(
$twitterMessage->getApiEndpoint(),
Expand Down Expand Up @@ -76,4 +77,43 @@ private function addImagesIfGiven(TwitterMessage $twitterMessage): object

return $twitterMessage;
}

/**
* If it is a status update message and videos are provided, add them.
*/
private function addVideosIfGiven(TwitterMessage $twitterMessage): object
{
if (is_a($twitterMessage, TwitterStatusUpdate::class) && $twitterMessage->getVideos()) {
$this->twitter->setTimeouts(10, 15);

$twitterMessage->videoIds = collect($twitterMessage->getVideos())->map(function (TwitterVideo $video) {
$media = $this->twitter->upload('media/upload', [
'media' => $video->getPath(),
'media_category' => 'tweet_video',
'media_type' => $video->getMimeType(),
], true);

$status = $this->twitter->mediaStatus($media->media_id_string);

$safety = 30; // We don't want to wait forever, stop after 30 checks.
while (($status->processing_info->state == 'pending' || $status->processing_info->state == 'in_progress') && $safety > 0) {
if (isset($status->processing_info->error)) {
break;
}

sleep($status->processing_info->check_after_secs);
$status = $this->twitter->mediaStatus($media->media_id_string);
$safety = $safety - 1;
}

if (isset($status->processing_info->error)) {
throw CouldNotSendNotification::videoCouldNotBeProcessed($status->processing_info->error->message);
}

return $status->media_id_string;
});
}

return $twitterMessage;
}
}
36 changes: 34 additions & 2 deletions src/TwitterStatusUpdate.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
class TwitterStatusUpdate extends TwitterMessage
{
public ?Collection $imageIds = null;
public ?Collection $videoIds = null;
private ?array $images = null;
private ?array $videos = null;
private ?int $inReplyToStatusId = null;

/**
Expand Down Expand Up @@ -45,6 +47,22 @@ public function withImage(array|string $images): static
return $this;
}

/**
* Set Twitter media files.
*
* @return $this
*/
public function withVideo(array|string $videos): static
{
$videos = is_array($videos) ? $videos : [$videos];

collect($videos)->each(function ($video) {
$this->videos[] = new TwitterVideo($video);
});

return $this;
}

/**
* Get Twitter images list.
*/
Expand All @@ -53,6 +71,14 @@ public function getImages(): ?array
return $this->images;
}

/**
* Get Twitter videos list.
*/
public function getVideos(): ?array
{
return $this->videos;
}

/**
* @param int $statusId
* @return $this
Expand All @@ -79,8 +105,14 @@ public function getRequestBody(): array
{
$body = ['status' => $this->getContent()];

if ($this->imageIds instanceof Collection) {
$body['media_ids'] = $this->imageIds->implode(',');
$mediaIds = collect()
->merge($this->imageIds instanceof Collection ? $this->imageIds : [])
->merge($this->videoIds instanceof Collection ? $this->videoIds : [])
->filter()
->values();

if ($mediaIds->count() > 0) {
$body['media_ids'] = $mediaIds->implode(',');
}

if ($this->inReplyToStatusId) {
Expand Down
20 changes: 20 additions & 0 deletions src/TwitterVideo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace NotificationChannels\Twitter;

class TwitterVideo
{
public function __construct(private string $videoPath)
{
}

public function getPath(): string
{
return $this->videoPath;
}

public function getMimeType(): string
{
return mime_content_type($this->videoPath);
}
}
92 changes: 92 additions & 0 deletions tests/TwitterChannelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,51 @@ public function it_can_send_a_status_update_notification_with_images()
$this->channel->send(new TestNotifiable(), new TestNotificationWithImage());
}

/** @test */
public function it_can_send_a_status_update_notification_with_videos()
{
$media = new stdClass;
$media->media_id_string = '2';

$status = new stdClass;
$status->media_id_string = '2';
$status->processing_info = new stdClass;
$status->processing_info->state = 'completed';

$this->twitter->shouldReceive('setTimeouts')
->once()
->with(10, 15);

$this->twitter->shouldReceive('post')
->once()
->with(
'statuses/update',
['status' => 'Laravel Notification Channels are awesome!', 'media_ids' => '2'],
false
)
->andReturn([]);

$this->twitter->shouldReceive('upload')
->once()
->with('media/upload', [
'media' => public_path('video.mp4'),
'media_category' => 'tweet_video',
'media_type' => 'video/mp4',
], true)
->andReturn($media);

$this->twitter->shouldReceive('mediaStatus')
->once()
->with($media->media_id_string)
->andReturn($status);

$this->twitter->shouldReceive('getLastHttpCode')
->once()
->andReturn(200);

$this->channel->send(new TestNotifiable(), new TestNotificationWithVideo());
}

/** @test */
public function it_can_send_a_status_update_notification_with_reply_to_status_id(): void
{
Expand Down Expand Up @@ -118,6 +163,42 @@ public function it_throws_an_exception_when_it_could_not_send_the_notification()

$this->channel->send(new TestNotifiable(), new TestNotification());
}

/** @test */
public function it_throws_an_exception_when_it_could_not_send_the_notification_with_videos()
{
$media = new stdClass;
$media->media_id_string = '2';

$status = new stdClass;
$status->media_id_string = '2';
$status->processing_info = new stdClass;
$status->processing_info->state = 'failed';
$status->processing_info->error = new stdClass;
$status->processing_info->error->message = 'invalid media';

$this->twitter->shouldReceive('setTimeouts')
->once()
->with(10, 15);

$this->twitter->shouldReceive('upload')
->once()
->with('media/upload', [
'media' => public_path('video.mp4'),
'media_category' => 'tweet_video',
'media_type' => 'video/mp4',
], true)
->andReturn($media);

$this->twitter->shouldReceive('mediaStatus')
->once()
->with($media->media_id_string)
->andReturn($status);

$this->expectException(CouldNotSendNotification::class);

$this->channel->send(new TestNotifiable(), new TestNotificationWithVideo());
}
}

class TestNotifiable
Expand Down Expand Up @@ -168,6 +249,17 @@ public function toTwitter(mixed $notifiable): TwitterMessage
}
}

class TestNotificationWithVideo extends Notification
{
/**
* @throws CouldNotSendNotification
*/
public function toTwitter(mixed $notifiable): TwitterMessage
{
return (new TwitterStatusUpdate('Laravel Notification Channels are awesome!'))->withVideo(public_path('video.mp4'));
}
}

class TestNotificationWithReplyToStatusId extends Notification
{
private int $replyToStatusId;
Expand Down
34 changes: 33 additions & 1 deletion tests/TwitterStatusUpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use NotificationChannels\Twitter\Exceptions\CouldNotSendNotification;
use NotificationChannels\Twitter\TwitterImage;
use NotificationChannels\Twitter\TwitterStatusUpdate;
use NotificationChannels\Twitter\TwitterVideo;

class TwitterStatusUpdateTest extends TestCase
{
Expand Down Expand Up @@ -48,15 +49,46 @@ public function it_accepts_array_of_image_paths(): void
$this->assertEquals($imagePathsObjects, $message->getImages());
}

/** @test */
public function video_paths_parameter_is_optional(): void
{
$message = new TwitterStatusUpdate('myMessage');

$this->assertEquals(null, $message->getVideos());
}

/** @test */
public function it_accepts_one_video_path(): void
{
$message = (new TwitterStatusUpdate('myMessage'))->withVideo('video.mp4');

$this->assertEquals('myMessage', $message->getContent());
$this->assertEquals([new TwitterVideo('video.mp4')], $message->getVideos());
}

/** @test */
public function it_accepts_array_of_video_paths(): void
{
$videoPaths = ['path1', 'path2'];
$message = (new TwitterStatusUpdate('myMessage'))->withVideo($videoPaths);
$videoPathsObjects = collect($videoPaths)->map(function ($video) {
return new TwitterVideo($video);
})->toArray();

$this->assertEquals('myMessage', $message->getContent());
$this->assertEquals($videoPathsObjects, $message->getVideos());
}

/** @test */
public function it_constructs_a_request_body(): void
{
$message = new TwitterStatusUpdate('myMessage');
$message->imageIds = collect([434, 435, 436]);
$message->videoIds = collect([534, 535, 536]);

$this->assertEquals([
'status' => 'myMessage',
'media_ids' => '434,435,436',
'media_ids' => '434,435,436,534,535,536',
], $message->getRequestBody());
}

Expand Down
23 changes: 23 additions & 0 deletions tests/TwitterVideoTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace NotificationChannels\Twitter;

function mime_content_type($path)
{
return 'video/mp4';
}

namespace NotificationChannels\Twitter\Test;

use NotificationChannels\Twitter\TwitterVideo;

class TwitterVideoTest extends TestCase
{
/** @test */
public function it_accepts_a_video_path_when_constructing_a_twitter_video(): void
{
$video = new TwitterVideo('/foo/bar/baz.mp4');
$this->assertEquals('/foo/bar/baz.mp4', $video->getPath());
$this->assertEquals('video/mp4', $video->getMimeType());
}
}

0 comments on commit 70c6553

Please sign in to comment.