Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kick off autoshare page (local only for now) #1483

Merged
merged 4 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,18 @@ APP_URL=http://localhost

# required for Laravel defautlst to work
DB_CONNECTION=sqlite

# @note this must be a use api key, not a project one
OPENAI_API_KEY=

TWITTER_API_KEY=
TWITTER_API_SECRET=

TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_SECRET=

TWITTER_BEARER_TOKEN=
TWITTER_APP_ID=

TWITTER_CLIENT_ID=
TWITTER_CLIENT_SECRET=
15 changes: 15 additions & 0 deletions .env.prod
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,18 @@ APP_URL=https://tomasvotruba.com

# required for Laravel defautlst to work
DB_CONNECTION=sqlite

# @note this must be a use api key, not a project one
OPENAI_API_KEY=

TWITTER_API_KEY=
TWITTER_API_SECRET=

TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_SECRET=

TWITTER_BEARER_TOKEN=
TWITTER_APP_ID=

TWITTER_CLIENT_ID=
TWITTER_CLIENT_SECRET=
4 changes: 4 additions & 0 deletions .github/workflows/code_analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ jobs:
# disable xdebug
coverage: none

# setup default envs, invoke by comopser install Laravel post-install event
- run: cp .env.local .env

# composer install cache - https://github.com/ramsey/composer-install
- uses: "ramsey/composer-install@v2"


- run: ${{ matrix.actions.run }}
3 changes: 3 additions & 0 deletions .github/workflows/rector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
with:
php-version: 8.2

# setup default envs, invoke by comopser install Laravel post-install event
- run: cp .env.local .env

- uses: "ramsey/composer-install@v1"

## First run Rector - here can't be --dry-run !!! it would stop the job with it and not commit anything in the future
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ cd tomasvotruba.com
composer install
```

- Build assets

```bash
yarn build

# or to watch files and reload on scss change
yarn dev
```

- Run local server

```bash
Expand Down
41 changes: 41 additions & 0 deletions app/Console/Commands/TweetPostCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Repository\PostRepository;
use Illuminate\Console\Command;
use Noweh\TwitterApi\Client;

final class TweetPostCommand extends Command
{
/**
* @var string
*/
protected $signature = 'app:tweet-post';

/**
* @var string
*/
protected $description = 'Tweet daily post';

public function __construct(
private readonly Client $twitterClient,
private readonly PostRepository $postRepository,
) {

parent::__construct();
}

public function handle(): void
{
$randomPost = $this->postRepository->fetchRandom(1);

$twitterResponse = $this->twitterClient->tweet()->create()->performRequest([
'text' => 'Test Tweet... ',
]);

$this->info('[DRY-RUN] Tweet is done!');
}
}
16 changes: 16 additions & 0 deletions app/Console/ConsoleKernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel;

final class ConsoleKernel extends Kernel
{
protected function schedule(Schedule $schedule): void
{
$schedule->command('app:tweet-post')->weekdays()->at('08:00')->timezone('Europe/Paris');
}
}
16 changes: 16 additions & 0 deletions app/Enum/GptModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Enum;

/**
* @see https://platform.openai.com/docs/models
*/
final readonly class GptModel
{
/**
* @var string
*/
public const GPT_4O_MINI = 'gpt-4o-mini';
}
25 changes: 4 additions & 21 deletions app/Http/Controllers/ShareBoardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@

namespace App\Http\Controllers;

use App\Entity\Post;
use App\Repository\PostRepository;
use App\Socials\PostTweetGenerator;
use App\ValueObject\PostTweet;
use Illuminate\Contracts\View\View;
use Illuminate\Routing\Controller;
use OpenAI\Client;

final class ShareBoardController extends Controller
{
public function __construct(
private readonly PostRepository $postRepository,
private readonly Client $client
private readonly PostTweetGenerator $postTweetGenerator,
) {
}

public function __invoke(): View
{
$randomPosts = $this->postRepository->fetchRandom(2);
$randomPosts = $this->postRepository->fetchRandom(4);

// @todo do parallel :)
$postTweets = [];
foreach ($randomPosts as $randomPost) {
$tweet = $this->createTweetForPost($randomPost);
$tweet = $this->postTweetGenerator->generateTweet($randomPost);
$postTweets[] = new PostTweet($tweet, $randomPost);
}

Expand All @@ -35,20 +34,4 @@ public function __invoke(): View
'postTweets' => $postTweets,
]);
}

private function createTweetForPost(Post $post): string
{
$createResponse = $this->client->chat()->create([
// @see https://platform.openai.com/docs/models
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'user',
'content' => 'Hello! I need help making a short engaging tweet for a blog post. Also add an emoji. No hash tags, no links, no quotes. Fit it 100-120 chars. Here is a blog post: ' . PHP_EOL . PHP_EOL . $post->getContent(),
],
],
]);

return (string) $createResponse->choices[0]->message->content;
}
}
30 changes: 27 additions & 3 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,34 @@ final class AppServiceProvider extends ServiceProvider
public function register(): void
{
$this->app->singleton(Client::class, function (): Client {
$openApiKey = getenv('OPENAI_API_KEY');
Assert::string($openApiKey);

$openApiKey = env('OPENAI_API_KEY');
Assert::string($openApiKey, 'OPEN_API_KEY is missing in .env');
return OpenAI::client($openApiKey);
});

$this->app->singleton(\Noweh\TwitterApi\Client::class, function (): \Noweh\TwitterApi\Client {
$twitterAppId = env('TWITTER_APP_ID');
$twitterApiKey = env('TWITTER_API_KEY');
$twitterApiSecret = env('TWITTER_API_SECRET');
$twitterAccessToken = env('TWITTER_ACCESS_TOKEN');
$twitterAccessSecret = env('TWITTER_ACCESS_SECRET');
$twitterBearerToken = env('TWITTER_BEARER_TOKEN');

Assert::string($twitterAppId, '"TWITTER_APP_ID" env value is missing in .env');
Assert::string($twitterApiKey, '"TWITTER_API_KEY" env value is missing in .env');
Assert::string($twitterApiSecret, '"TWITTER_API_SECRET" env value is missing in .env');
Assert::string($twitterAccessToken, '"TWITTER_ACCESS_TOKEN" env value is missing in .env');
Assert::string($twitterAccessSecret, '"TWITTER_ACCESS_SECRET" env value is missing in .env');
Assert::string($twitterBearerToken, '"TWITTER_BEARER_TOKEN" env value is missing in .env');

return new \Noweh\TwitterApi\Client([
'account_id' => $twitterAppId,
'access_token' => $twitterAccessToken,
'access_token_secret' => $twitterAccessSecret,
'consumer_key' => $twitterApiKey,
'consumer_secret' => $twitterApiSecret,
'bearer_token' => $twitterBearerToken,
]);
});
}
}
32 changes: 32 additions & 0 deletions app/Socials/PostTweetGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace App\Socials;

use App\Entity\Post;
use App\Enum\GptModel;
use OpenAI\Client;

final readonly class PostTweetGenerator
{
public function __construct(
private readonly Client $openAIClient,
) {
}

public function generateTweet(Post $post): string
{
$createResponse = $this->openAIClient->chat()->create([
'model' => GptModel::GPT_4O_MINI,
'messages' => [
[
'role' => 'user',
'content' => 'Hello! Please make a short engaging tweet for a blog post. Also add an emoji. No hash tags, no links, no quotes. Fit it 100-140 chars. Here is the blog post: ' . PHP_EOL . PHP_EOL . $post->getContent(),
],
],
]);

return (string) $createResponse->choices[0]->message->content;
}
}
9 changes: 3 additions & 6 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@
])
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
)
->withMiddleware(function (Middleware $middleware): void {
//
})
->withExceptions(function (Exceptions $exceptions): void {
//
})
->withMiddleware(function (Middleware $middleware): void {})
->withExceptions(function (Exceptions $exceptions): void {})
->create();
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
"ext-gd": "*",
"ext-mbstring": "*",
"imagine/imagine": "^1.3",
"laravel/framework": "^11.0",
"league/commonmark": "^2.4.1",
"nesbot/carbon": "^2.8",
"laravel/framework": "^11.21",
"league/commonmark": "^2.5",
"nesbot/carbon": "^2.72",
"nette/utils": "^4.0",
"noweh/twitter-api-v2-php": "^3.5",
"openai-php/client": "^0.10.1",
"symfony/yaml": "^6.4"
},
Expand Down
Loading
Loading