From 427228a1d2529cfa289df8c3be7bd50d79456808 Mon Sep 17 00:00:00 2001 From: Flug Date: Fri, 26 Jun 2020 00:50:05 +0200 Subject: [PATCH] add twitter --- .env.dist | 5 + .gitignore | 3 +- assets/js/app.js | 2 - assets/sass/app.sass | 4 + composer.json | 1 + composer.lock | 56 +++++++++- config/packages/framework.yaml | 2 +- config/packages/messenger.yaml | 5 + config/services.yaml | 12 ++ package.json | 3 +- src/Application/Handler/DoDoAddAccount.php | 39 +++++++ src/Application/Handler/DoRemoveAccount.php | 35 ++++++ src/Application/Repository/Accounts.php | 41 +++++++ src/Domain/Command/Twitter/Account/Add.php | 23 ++++ src/Domain/Command/Twitter/Account/Delete.php | 23 ++++ src/Domain/Handler/Twitter/DoAddAccount.php | 11 ++ .../Handler/Twitter/DoRemoveAccount.php | 12 ++ src/Domain/Model/Twitter/Account.php | 32 ++++++ src/Domain/Repository/.gitignore | 0 src/Domain/Repository/Twitter/Accounts.php | 17 +++ .../Symfony/Controller/Twitter.php | 80 ++++++++++++++ .../Controller/Twitter/DeleteAccount.php | 46 ++++++++ src/Infrastructure/Twitter/Client.php | 103 ++++++++++++++++++ src/Infrastructure/Twitter/Client/Factory.php | 19 ++++ symfony.lock | 3 + templates/base.html.twig | 9 ++ templates/twitter/index.html.twig | 72 ++++++++++++ templates/twitter/tweet/box.html.twig | 39 +++++++ tests/bootstrap.php | 11 ++ translations/messages.en.yml | 2 + translations/messages.fr.yml | 2 + yarn.lock | 12 ++ 32 files changed, 718 insertions(+), 6 deletions(-) create mode 100644 src/Application/Handler/DoDoAddAccount.php create mode 100644 src/Application/Handler/DoRemoveAccount.php create mode 100644 src/Application/Repository/Accounts.php create mode 100644 src/Domain/Command/Twitter/Account/Add.php create mode 100644 src/Domain/Command/Twitter/Account/Delete.php create mode 100644 src/Domain/Handler/Twitter/DoAddAccount.php create mode 100644 src/Domain/Handler/Twitter/DoRemoveAccount.php create mode 100644 src/Domain/Model/Twitter/Account.php create mode 100644 src/Domain/Repository/.gitignore create mode 100644 src/Domain/Repository/Twitter/Accounts.php create mode 100644 src/Infrastructure/Symfony/Controller/Twitter.php create mode 100644 src/Infrastructure/Symfony/Controller/Twitter/DeleteAccount.php create mode 100644 src/Infrastructure/Twitter/Client.php create mode 100644 src/Infrastructure/Twitter/Client/Factory.php create mode 100644 templates/twitter/index.html.twig create mode 100644 templates/twitter/tweet/box.html.twig create mode 100644 tests/bootstrap.php diff --git a/.env.dist b/.env.dist index 2e92769..552003b 100644 --- a/.env.dist +++ b/.env.dist @@ -31,6 +31,11 @@ VEILLEUR_REPOSITORY_LANGUAGE='["php", "javascript", "java", "rust"]' # For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8" # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml DATABASE_URL='' +TWITTER_CONSUMER_KEY= +TWITTER_CONSUMER_SECRET= +TWITTER_ACCESS_TOKEN_KEY= +TWITTER_ACCESS_TOKEN_SECRET= + ###< doctrine/doctrine-bundle ### ###> symfony/messenger ### diff --git a/.gitignore b/.gitignore index ba27aaf..95c36fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ - +db ###> symfony/framework-bundle ### /.env.local +.env /.env.local.php /.env.*.local /config/secrets/prod/prod.decrypt.private.php diff --git a/assets/js/app.js b/assets/js/app.js index b491d3b..3368dc1 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -7,8 +7,6 @@ // any CSS you import will output into a single css file (app.css in this case) import '../sass/app.sass'; - // Need jQuery? Install it with "yarn add jquery", then uncomment to import it. // import $ from 'jquery'; -console.log('Hello Webpack Encore! Edit me in assets/js/app.js'); diff --git a/assets/sass/app.sass b/assets/sass/app.sass index 79367e7..1054464 100644 --- a/assets/sass/app.sass +++ b/assets/sass/app.sass @@ -7,3 +7,7 @@ font-size: 11px color: #24292e list-style-type: none + +.tweet-list + li + list-style-type: none diff --git a/composer.json b/composer.json index 3b29680..a17e244 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "php": "^7.4", "ext-ctype": "*", "ext-iconv": "*", + "abraham/twitteroauth": "^1.1", "sensio/framework-extra-bundle": "^5.1", "symfony/asset": "5.1.*", "symfony/console": "5.1.*", diff --git a/composer.lock b/composer.lock index b00e022..9406c80 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,62 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0053c5b9e664c1d69c1ce6143be3d9fb", + "content-hash": "24e782285cfe21066c39822fa480e8f3", "packages": [ + { + "name": "abraham/twitteroauth", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/abraham/twitteroauth.git", + "reference": "d54b71c2eee94252154e7b50656e17422fa0b9e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/abraham/twitteroauth/zipball/d54b71c2eee94252154e7b50656e17422fa0b9e1", + "reference": "d54b71c2eee94252154e7b50656e17422fa0b9e1", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": "^7.2 || ^7.3" + }, + "require-dev": { + "phpmd/phpmd": "~2.6", + "phpunit/phpunit": "~5.7", + "squizlabs/php_codesniffer": "~3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Abraham\\TwitterOAuth\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abraham Williams", + "email": "abraham@abrah.am", + "homepage": "https://abrah.am", + "role": "Developer" + } + ], + "description": "The most popular PHP library for use with the Twitter OAuth REST API.", + "homepage": "https://twitteroauth.com", + "keywords": [ + "Twitter API", + "Twitter oAuth", + "api", + "oauth", + "rest", + "social", + "twitter" + ], + "time": "2019-11-29T14:55:32+00:00" + }, { "name": "doctrine/annotations", "version": "1.10.3", diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 3c89659..14b2564 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,5 +1,5 @@ parameters: - frenquency_available: [daily, weekly , monthly] + frenquency_available: [today, daily, weekly , monthly] languages: '%env(json:VEILLEUR_REPOSITORY_LANGUAGE)%' # see https://symfony.com/doc/current/reference/configuration/framework.html diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml index 6b7319c..51f38bd 100644 --- a/config/packages/messenger.yaml +++ b/config/packages/messenger.yaml @@ -12,3 +12,8 @@ framework: routing: # Route your messages to the transports # 'App\Message\YourMessage': async + buses: + command_bus: + middleware: + - doctrine_ping_connection + - doctrine_transaction diff --git a/config/services.yaml b/config/services.yaml index 60d0870..240f645 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -29,5 +29,17 @@ services: class: Symfony\Component\HttpClient\NativeHttpClient factory: ['@Veilleur\Infrastructure\Github\Trending\Client\Factory', create] + twitter.client.factory: + class: Abraham\TwitterOAuth\TwitterOAuth + factory: ['@Veilleur\Infrastructure\Twitter\Client\Factory', create] + arguments: + - '%env(TWITTER_CONSUMER_KEY)%' + - '%env(TWITTER_CONSUMER_SECRET)%' + - '%env(TWITTER_ACCESS_TOKEN_KEY)%' + - '%env(TWITTER_ACCESS_TOKEN_SECRET)%' + Veilleur\Infrastructure\Github\Trending\Client: - '@github.client.factory' + + Veilleur\Infrastructure\Twitter\Client: + - '@twitter.client.factory' diff --git a/package.json b/package.json index cf37f0c..97df323 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "build": "encore production --progress" }, "dependencies": { - "@primer/css": "^14.4.0" + "@primer/css": "^14.4.0", + "react-twitter-widgets": "^1.9.5" } } diff --git a/src/Application/Handler/DoDoAddAccount.php b/src/Application/Handler/DoDoAddAccount.php new file mode 100644 index 0000000..eb23195 --- /dev/null +++ b/src/Application/Handler/DoDoAddAccount.php @@ -0,0 +1,39 @@ +accounts = $accounts; + } + + public function __invoke(AddAccount $accountCommand) + { + + $account = $this->accounts->findOneByUsername($accountCommand->getUsername()); + if ($account instanceof \Veilleur\Domain\Model\Twitter\Account) { + return; + } + $account = new \Veilleur\Domain\Model\Twitter\Account($accountCommand->getUsername()); + $this->accounts->persist($account); + + } + + public static function getHandledMessages(): iterable + { + yield AddAccount::class; + } +} diff --git a/src/Application/Handler/DoRemoveAccount.php b/src/Application/Handler/DoRemoveAccount.php new file mode 100644 index 0000000..bb81f39 --- /dev/null +++ b/src/Application/Handler/DoRemoveAccount.php @@ -0,0 +1,35 @@ +accounts = $accounts; + } + + public function __invoke(Delete $delete) + { + + $account = $this->accounts->findOneByUsername($delete->getUsername()); + $this->accounts->remove($account); + + } + + public static function getHandledMessages(): iterable + { + yield Delete::class; + } +} diff --git a/src/Application/Repository/Accounts.php b/src/Application/Repository/Accounts.php new file mode 100644 index 0000000..07b16f9 --- /dev/null +++ b/src/Application/Repository/Accounts.php @@ -0,0 +1,41 @@ +em = $registry->getManager(); + $this->innerRepository = $registry->getRepository(Account::class); + } + + public function findAll(): iterable + { + return $this->innerRepository->findAll(); + } + + public function findOneByUsername(string $username): ?Account + { + return $this->innerRepository->find($username); + } + + public function persist(Account $account): void + { + $this->em->persist($account); + } + + public function remove(?Account $account) + { + $this->em->remove($account); + } +} diff --git a/src/Domain/Command/Twitter/Account/Add.php b/src/Domain/Command/Twitter/Account/Add.php new file mode 100644 index 0000000..9b96c0c --- /dev/null +++ b/src/Domain/Command/Twitter/Account/Add.php @@ -0,0 +1,23 @@ +username = $username; + } + + /** + * @return string + */ + public function getUsername(): string + { + return $this->username; + } + +} diff --git a/src/Domain/Command/Twitter/Account/Delete.php b/src/Domain/Command/Twitter/Account/Delete.php new file mode 100644 index 0000000..b0e0959 --- /dev/null +++ b/src/Domain/Command/Twitter/Account/Delete.php @@ -0,0 +1,23 @@ +username = $username; + } + + /** + * @return string + */ + public function getUsername(): string + { + return $this->username; + } + +} diff --git a/src/Domain/Handler/Twitter/DoAddAccount.php b/src/Domain/Handler/Twitter/DoAddAccount.php new file mode 100644 index 0000000..a3ca296 --- /dev/null +++ b/src/Domain/Handler/Twitter/DoAddAccount.php @@ -0,0 +1,11 @@ +username = $username; + } + + /** + * @return mixed + */ + public function getUsername() + { + return $this->username; + } +} diff --git a/src/Domain/Repository/.gitignore b/src/Domain/Repository/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Domain/Repository/Twitter/Accounts.php b/src/Domain/Repository/Twitter/Accounts.php new file mode 100644 index 0000000..3694fe3 --- /dev/null +++ b/src/Domain/Repository/Twitter/Accounts.php @@ -0,0 +1,17 @@ +twig = $twig; + $this->bus = $bus; + $this->router = $router; + $this->accounts = $accounts; + $this->twitterClient = $twitterClient; + } + + public function __invoke(Request $request) + { + + if ($request->isMethod('POST')) { + + $this->bus->dispatch(new AddAccount($request->request->get('username'))); + + return new RedirectResponse( + $this->router->generate('twitter', + ['username' => $request->request->get('username')] + ) + ); + } + $tweets = []; + if ($request->query->has('username')) { + $tweets = $this->twitterClient->getUserTimeline($request->query->get('username')); + } + + return new Response($this->twig->render('twitter/index.html.twig', [ + 'accounts' => $this->accounts->findAll(), + 'tweets' => $tweets, + ])); + } +} diff --git a/src/Infrastructure/Symfony/Controller/Twitter/DeleteAccount.php b/src/Infrastructure/Symfony/Controller/Twitter/DeleteAccount.php new file mode 100644 index 0000000..7ea0a13 --- /dev/null +++ b/src/Infrastructure/Symfony/Controller/Twitter/DeleteAccount.php @@ -0,0 +1,46 @@ +accounts = $accounts; + $this->router = $router; + $this->bus = $bus; + } + + public function __invoke(Request $request) + { + + $this->bus->dispatch(new Delete($request->query->get('username'))); + + return new RedirectResponse($this->router->generate('twitter')); + } +} diff --git a/src/Infrastructure/Twitter/Client.php b/src/Infrastructure/Twitter/Client.php new file mode 100644 index 0000000..ba0eb19 --- /dev/null +++ b/src/Infrastructure/Twitter/Client.php @@ -0,0 +1,103 @@ +auth = $auth; + $this->auth->setTimeouts(20, 50); + } + + public function send(array $message, ?string $image = null) + { + if ($image !== null) { + $message = array_merge($this->uploadMedia($image), $message); + } + + return $this->auth->post("statuses/update", $message); + } + + private function uploadMedia(string $path = null): ?array + { + if ($path === null) { + return null; + } + $mediaPath = $path; + $readmeMedia = (array)$this->auth->upload('media/upload', + ['media' => $mediaPath]); + + return ['media_ids' => [$readmeMedia['media_id_string']]]; + } + + public function verify() + { + return (array)$this->auth->get("account/verify_credentials"); + + } + + public function findAUser( + string $name + ): ?array { + $response = $this->auth->get('users/search', [ + 'q' => strtr(strtolower($name), [' ' => '%20']), + 'include_entities' => true, + 'count' => 20, + ]); + foreach ($response as $user) { + if (property_exists($user->entities, 'url')) { + foreach ($user->entities->url->urls as $url) { + if (!property_exists($url, 'display_url')) { + return []; + } + if ((bool)strpos($url->display_url, 'github') === true) { + return (array)$user; + } + } + } + } + + return []; + } + + public function lastPossibilityUser(string $name) + { + $response = $this->auth->get('users/search', [ + 'q' => strtr($name, [' ' => '%20']), + ]); + + return (array)current($response); + } + + public function getLookUp(string $name) + { + $response = $this->auth->get('users/lookup', [ + 'screen_name' => $name, + ]); + + return (array)$response; + } + + public function getUserTimeline(string $username) + { + + $response = $this->auth->get('statuses/user_timeline', [ + 'screen_name' => $username, + 'tweet_mode'=>'extended', + 'include_entities' => true + ]); + + return (array)$response; + } + + public function isConnected(): bool + { + return !array_key_exists('errors', $this->verify()); + } +} diff --git a/src/Infrastructure/Twitter/Client/Factory.php b/src/Infrastructure/Twitter/Client/Factory.php new file mode 100644 index 0000000..7ec5aef --- /dev/null +++ b/src/Infrastructure/Twitter/Client/Factory.php @@ -0,0 +1,19 @@ + GitHub + +
+ + + + + Twitter +
{% block body %}{% endblock %} diff --git a/templates/twitter/index.html.twig b/templates/twitter/index.html.twig new file mode 100644 index 0000000..a6a0a76 --- /dev/null +++ b/templates/twitter/index.html.twig @@ -0,0 +1,72 @@ +{% extends 'base.html.twig' %} +{% block javascripts %} + {{ parent() }} + +{% endblock %} +{% block body %} +
+
+ +
+
+ {{ 'twitter.account' |trans }} +
+
    +
  • +
    +
    + + + + +
    +
    + + +
  • + +
+
+
+
+
+ {{ 'twitter.account' |trans }} +
+ +
+ +
+ +
+ {% for tweet in tweets %} +
+ {% include 'twitter/tweet/box.html.twig' %} +
+ {% endfor %} +
+ +
+ +{% endblock %} diff --git a/templates/twitter/tweet/box.html.twig b/templates/twitter/tweet/box.html.twig new file mode 100644 index 0000000..07b5f09 --- /dev/null +++ b/templates/twitter/tweet/box.html.twig @@ -0,0 +1,39 @@ + +
+
+

+
+
+ +
+ + + +
+
+
+ + +
+

+
+
+ {{ tweet.full_text }} +
+ + +
diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..469dcce --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,11 @@ +bootEnv(dirname(__DIR__).'/.env'); +} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 2c47a5e..0348367 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -1,4 +1,6 @@ languages: 'Languages' any: 'all' since: 'Since' +twitter: + account: 'Account Twitter' diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 32364c7..eaa9802 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -1,3 +1,5 @@ languages: 'Langages' any: 'tous' since: 'Depuis' +twitter: + account: 'Compte Twitter' diff --git a/yarn.lock b/yarn.lock index 7f658d1..aa85c60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4144,6 +4144,11 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +loadjs@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loadjs/-/loadjs-4.2.0.tgz#2a0336376397a6a43edf98c9ec3229ddd5abb6f6" + integrity sha512-AgQGZisAlTPbTEzrHPb6q+NYBMD+DP9uvGSIjSUM5uG+0jG15cb8axWpxuOIqrmQjn6scaaH8JwloiP27b2KXA== + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -5515,6 +5520,13 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +react-twitter-widgets@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/react-twitter-widgets/-/react-twitter-widgets-1.9.5.tgz#3c05c0e6f54e79b175dd5e88b8870411e15c5790" + integrity sha512-C12GZzY0i7HsaQHOvfVswAJ0fj94VnC7freDK3Zb72tGEnLDwWDKApZ0yKoO4ywNqa/fcDNCU7JN9FUbQLO5VA== + dependencies: + loadjs "^4.2.0" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"