diff --git a/.env.dist b/.env.dist index 630ad081..4e8a4216 100644 --- a/.env.dist +++ b/.env.dist @@ -9,12 +9,6 @@ SLACK_CHANNEL_GITHUB=github SLACK_SIGNING_SECRET= SLACK_TOKEN= SLACK_WEBHOOK_LOGGING= -TWEET_VERIFICATION_TOKEN= -TWITTER_ACCESS_TOKEN= -TWITTER_ACCESS_SECRET= -TWITTER_CONSUMER_KEY= -TWITTER_CONSUMER_SECRET= MASTODON_CLIENT_ID= MASTODON_CLIENT_SECRET= MASTODON_BEARER_TOKEN= - diff --git a/README.md b/README.md index 435f1729..68fc7b01 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ The Laminas Bot acts as: - Report repository build status to the Laminas Slack #github channel. - Report releases to: - The Laminas Slack #github channel. - - The [@getlaminas](https://twitter.com/getlaminas) twitter account. - The https://getlaminas API, to allow rebuilding the release RSS feed. - A Discourse webhook handler: @@ -41,18 +40,6 @@ The Laminas Bot acts as: given repository. - /regenerate-tsc-list triggers rebuilding the list of TSC members (the only ones authorized to initiate slash commands). - - `/tweet [media:url] message` allows sending a tweet via the - [@getlaminas](https://twitter.com/getlaminas) twitter account, optionally - with media. - - `/retweet {url}` allows retweeting a tweet as specified by `{url}` via the - [@getlaminas](https://twitter.com/getlaminas) twitter account. - - `/reply-to-tweet {url} {message}` allows replying to a tweet as specified by - `{url}` via the [@getlaminas](https://twitter.com/getlaminas) twitter account. - -- A webhook for incoming tweets. This webhook works in conjunction with a recipe - on IFTTT that monitors tweets on the getlaminas account, and then passes them - to our webhook. The webhook queues a job that sends the tweet details to a - configured Slack channel. ### Requirements @@ -77,9 +64,6 @@ The Laminas Bot acts as: - /regenerate-tsc-list - /register-repo {repo} - /build-docs {repo} - - /tweet [media:url] {message} - - /retweet {url} - - /reply-to-tweet {url} {message} - Setup an incoming webhook - Add location to SLACK_WEBHOOK_LOGGING - Invite the bot to #technical-steering-committee and #laminasbot-errors @@ -93,15 +77,6 @@ The Laminas Bot acts as: - From Discourse - We need a signing secret; this can be arbitrary, but once used, must be used when registering ALL webhooks. Add to DISCOURSE_SECRET - -- From Twitter - - We need the access token and secret, and the consumer key and secret, as: - - TWITTER_ACCESS_TOKEN - - TWITTER_ACCESS_SECRET - - TWITTER_CONSUMER_KEY - - TWITTER_CONSUMER_SECRET - - We need the shared token between IFTTT and the website for accepting tweets - as TWEET_VERIFICATION_TOKEN. - From Mastodon - We need access credentials from a created app as @@ -116,7 +91,7 @@ The Laminas Bot acts as: #### Handlers -There are four primary endpoints: +There are three primary endpoints: - `/api/discourse/[:channel]/:event` is an incoming webhook from Discourse. The `:channel` is the name of the Slack channel to notify, and the `:event` @@ -136,12 +111,6 @@ There are four primary endpoints: implementation and dispatch. Individual implementations marshal appropriate events to dispatch via the event dispatcher. -- `/api/twitter/[:token]` is an incoming webhook from IFTTT. The `:token` is the - shared token between the IFTTT and the webhook; if not identical, requests are - rejected. They payload must contain the elements text, url, and timestamp, and - is used to dispatch an `App\Twitter\Tweet` event before returning a 202 - response. - #### Events The repository provides the following event types, with the listed handlers. @@ -153,14 +122,10 @@ Event Type | Handler(s) | Action taken `App\GitHub\Event\GitHubIssue` | `App\GitHub\Listener\GitHubIssueListener` | Send a notification to Slack about issue creation or closure. `App\GitHub\Event\GitHubIssueComment` | `App\GitHub\Listener\GitHubIssueCommentListener` | Send a notification to Slack about comment creation on an issue or pull request. `App\GitHub\Event\GitHubPullRequest` | `App\GitHub\Listener\GitHubPullRequestListener` | Send a notification to Slack about pull request creation or closure. -`App\GitHub\Event\GitHubRelease` | `App\GitHub\Listener\GitHubRelease\SlackListener`, `App\GitHub\Listener\GitHubReleaseTweetListener`, `App\GitHub\Listener\GitHubReleaseWebsiteUpdateListener` | Send a notification to Slack about release creation; tweet the release details; and notify the website of the release so it can update the release RSS feed. +`App\GitHub\Event\GitHubRelease` | `App\GitHub\Listener\GitHubRelease\SlackListener`, `App\GitHub\Listener\GitHubReleaseWebsiteUpdateListener` | Send a notification to Slack about release creation and notify the website of the release so it can update the release RSS feed. `App\GitHub\Event\GitHubStatus` | `App\GitHub\Listener\GitHubStatusListener` | Send a notification to Slack about a build failure, error, or success. `App\GitHub\Event\RegisterWebhook` | `App\GitHub\Listener\RegisterWebhookListener` | Use the GitHub API to register the Laminas-BOT webhook with the given repository. `App\Slack\Event\RegenerateAuthorizedUserList` | `App\Slack\Listener\RegenerateAuthorizedUserListListener` | Use the Slack Web API to rebuild the list of authorized slash command users from the current #technical-steering-committee list. -`App\Slack\Event\Retweet` | `App\Slack\Listener\RetweetListener` | Use the Twitter API to retweet a given tweet. -`App\Slack\Event\Tweet` | `App\Slack\Listener\TweetListener` | Use the Twitter API to send a tweet. -`App\Slack\Event\TwitterReply` | `App\Slack\Listener\TwitterReplyListener` | Use the Twitter API to send a message in reply to another tweet. -`App\Twitter\Tweet` | `App\Twitter\TweetListener` | Send a Slack message to a configured channel with details on a single tweet. All listeners are decorated using the `DeferredServiceListenerDelegator` class from the phly/phly-swoole-taskworker package. This means that they will be @@ -236,6 +201,3 @@ All of these objects support: technical steering committee members. If not, an error message is returned to the user immediately. The list is generated on application initialization, and again on receipt of a /regenerate-tsc-list command. - -- A shared secret is configured between IFTTT and the Twitter webhook, and we - also verify the payload structure. diff --git a/composer.json b/composer.json index 16193485..a19a9d5a 100644 --- a/composer.json +++ b/composer.json @@ -18,9 +18,9 @@ "ext-hash": "*", "ext-json": "*", "ext-openswoole": "*", - "beberlei/assert": "^2.9.9 || ^3.0.0", + "beberlei/assert": "^2.9.9 || ^3.3.2", "colorfield/mastodon-api": "^0.1.5", - "guzzlehttp/guzzle": "^6.3 || ^7.0", + "guzzlehttp/guzzle": "^6.3 || ^7.8", "laminas/laminas-cli": "^1.9.0", "laminas/laminas-component-installer": "^3.4.0", "laminas/laminas-config-aggregator": "^1.14.0", @@ -30,7 +30,6 @@ "laminas/laminas-servicemanager": "^3.22.1", "laminas/laminas-stdlib": "^3.18.0", "laminas/laminas-stratigility": "^3.11.0", - "laminas/laminas-twitter": "^3.7.0", "league/commonmark": "^2.4.1", "mezzio/mezzio": "^3.18.0", "mezzio/mezzio-fastroute": "^3.11.0", @@ -52,14 +51,14 @@ "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { - "filp/whoops": "^2.14.5", + "filp/whoops": "^2.15.3", "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-development-mode": "^3.1", - "mezzio/mezzio-tooling": "^2.5", - "phpspec/prophecy-phpunit": "^2.0.1", - "phpunit/phpunit": "^9.5.26", + "laminas/laminas-development-mode": "^3.11", + "mezzio/mezzio-tooling": "^2.8", + "phpspec/prophecy-phpunit": "^2.0.2", + "phpunit/phpunit": "^9.6.13", "roave/security-advisories": "dev-master", - "squizlabs/php_codesniffer": "^3.7.1" + "squizlabs/php_codesniffer": "^3.7.2" }, "config": { "discard-changes": true, diff --git a/composer.lock b/composer.lock index 7ca391ef..014f9591 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9d910c8336f5f07e99ddad02ae4ed202", + "content-hash": "8d781ae40d9acf427a14d4134d9b90f7", "packages": [ { "name": "beberlei/assert", @@ -895,74 +895,6 @@ ], "time": "2023-11-21T15:32:55+00:00" }, - { - "name": "laminas/laminas-config", - "version": "3.9.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-config.git", - "reference": "e53717277f6c22b1c697a46473b9a5ec9a438efa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-config/zipball/e53717277f6c22b1c697a46473b9a5ec9a438efa", - "reference": "e53717277f6c22b1c697a46473b9a5ec9a438efa", - "shasum": "" - }, - "require": { - "ext-json": "*", - "laminas/laminas-stdlib": "^3.6", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", - "psr/container": "^1.0" - }, - "conflict": { - "container-interop/container-interop": "<1.2.0", - "zendframework/zend-config": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~2.4.0", - "laminas/laminas-filter": "~2.23.0", - "laminas/laminas-i18n": "~2.19.0", - "laminas/laminas-servicemanager": "~3.19.0", - "phpunit/phpunit": "~9.5.25" - }, - "suggest": { - "laminas/laminas-filter": "^2.7.2; install if you want to use the Filter processor", - "laminas/laminas-i18n": "^2.7.4; install if you want to use the Translator processor", - "laminas/laminas-servicemanager": "^2.7.8 || ^3.3; if you need an extensible plugin manager for use with the Config Factory" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Config\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://laminas.dev", - "keywords": [ - "config", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-config/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-config/issues", - "rss": "https://github.com/laminas/laminas-config/releases.atom", - "source": "https://github.com/laminas/laminas-config" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-09-19T12:02:54+00:00" - }, { "name": "laminas/laminas-config-aggregator", "version": "1.14.0", @@ -1091,70 +1023,6 @@ ], "time": "2023-12-05T03:30:48+00:00" }, - { - "name": "laminas/laminas-crypt", - "version": "3.11.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-crypt.git", - "reference": "098fc61a895d1ff5d1c2b861525b4428bf6c3240" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/098fc61a895d1ff5d1c2b861525b4428bf6c3240", - "reference": "098fc61a895d1ff5d1c2b861525b4428bf6c3240", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "laminas/laminas-math": "^3.4", - "laminas/laminas-servicemanager": "^3.11.2", - "laminas/laminas-stdlib": "^3.8", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", - "psr/container": "^1.1" - }, - "conflict": { - "zendframework/zend-crypt": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~2.4.0", - "phpunit/phpunit": "^9.5.25" - }, - "suggest": { - "ext-openssl": "Required for most features of Laminas\\Crypt" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Crypt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Strong cryptography tools and password hashing", - "homepage": "https://laminas.dev", - "keywords": [ - "crypt", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-crypt/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-crypt/issues", - "rss": "https://github.com/laminas/laminas-crypt/releases.atom", - "source": "https://github.com/laminas/laminas-crypt" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-11-06T23:02:42+00:00" - }, { "name": "laminas/laminas-diactoros", "version": "3.3.0", @@ -1302,86 +1170,6 @@ ], "time": "2023-10-10T08:35:13+00:00" }, - { - "name": "laminas/laminas-feed", - "version": "2.22.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-feed.git", - "reference": "669792b819fca7274698147ad7a2ecc1b0a9b141" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/669792b819fca7274698147ad7a2ecc1b0a9b141", - "reference": "669792b819fca7274698147ad7a2ecc1b0a9b141", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "laminas/laminas-escaper": "^2.9", - "laminas/laminas-stdlib": "^3.6", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" - }, - "conflict": { - "laminas/laminas-servicemanager": "<3.3", - "zendframework/zend-feed": "*" - }, - "require-dev": { - "laminas/laminas-cache": "^2.13.2 || ^3.11", - "laminas/laminas-cache-storage-adapter-memory": "^1.1.0 || ^2.2", - "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-db": "^2.18", - "laminas/laminas-http": "^2.18", - "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-validator": "^2.38", - "phpunit/phpunit": "^10.3.1", - "psalm/plugin-phpunit": "^0.18.4", - "psr/http-message": "^2.0", - "vimeo/psalm": "^5.14.1" - }, - "suggest": { - "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", - "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", - "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", - "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", - "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Feed\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides functionality for creating and consuming RSS and Atom feeds", - "homepage": "https://laminas.dev", - "keywords": [ - "atom", - "feed", - "laminas", - "rss" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-feed/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-feed/issues", - "rss": "https://github.com/laminas/laminas-feed/releases.atom", - "source": "https://github.com/laminas/laminas-feed" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-10-11T20:16:37+00:00" - }, { "name": "laminas/laminas-http", "version": "2.19.0", @@ -1514,91 +1302,6 @@ ], "time": "2023-09-04T10:43:03+00:00" }, - { - "name": "laminas/laminas-i18n", - "version": "2.24.1", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-i18n.git", - "reference": "dafb5eddfb43575befd29aeb195c55f92834fd32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/dafb5eddfb43575befd29aeb195c55f92834fd32", - "reference": "dafb5eddfb43575befd29aeb195c55f92834fd32", - "shasum": "" - }, - "require": { - "ext-intl": "*", - "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-stdlib": "^3.0", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" - }, - "conflict": { - "laminas/laminas-view": "<2.20.0", - "zendframework/zend-i18n": "*" - }, - "require-dev": { - "laminas/laminas-cache": "^3.11.0", - "laminas/laminas-cache-storage-adapter-memory": "^2.3.0", - "laminas/laminas-cache-storage-deprecated-factory": "^1.1", - "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-config": "^3.9.0", - "laminas/laminas-eventmanager": "^3.12", - "laminas/laminas-filter": "^2.33", - "laminas/laminas-validator": "^2.41", - "laminas/laminas-view": "^2.32", - "phpunit/phpunit": "^10.4.2", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.15.0" - }, - "suggest": { - "laminas/laminas-cache": "You should install this package to cache the translations", - "laminas/laminas-config": "You should install this package to use the INI translation format", - "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", - "laminas/laminas-filter": "You should install this package to use the provided filters", - "laminas/laminas-i18n-resources": "This package provides validator and captcha translations", - "laminas/laminas-validator": "You should install this package to use the provided validators", - "laminas/laminas-view": "You should install this package to use the provided view helpers" - }, - "type": "library", - "extra": { - "laminas": { - "component": "Laminas\\I18n", - "config-provider": "Laminas\\I18n\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Laminas\\I18n\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Provide translations for your application, and filter and validate internationalized values", - "homepage": "https://laminas.dev", - "keywords": [ - "i18n", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-i18n/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-i18n/issues", - "rss": "https://github.com/laminas/laminas-i18n/releases.atom", - "source": "https://github.com/laminas/laminas-i18n" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-11-08T08:56:45+00:00" - }, { "name": "laminas/laminas-loader", "version": "2.10.0", @@ -1655,135 +1358,6 @@ ], "time": "2023-10-18T09:58:51+00:00" }, - { - "name": "laminas/laminas-math", - "version": "3.7.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-math.git", - "reference": "3e90445828fd64308de2a600b48c3df051b3b17a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-math/zipball/3e90445828fd64308de2a600b48c3df051b3b17a", - "reference": "3e90445828fd64308de2a600b48c3df051b3b17a", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" - }, - "conflict": { - "zendframework/zend-math": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~2.4.0", - "phpunit/phpunit": "~9.5.25" - }, - "suggest": { - "ext-bcmath": "If using the bcmath functionality", - "ext-gmp": "If using the gmp functionality" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laminas\\Math\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Create cryptographically secure pseudo-random numbers, and manage big integers", - "homepage": "https://laminas.dev", - "keywords": [ - "laminas", - "math" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-math/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-math/issues", - "rss": "https://github.com/laminas/laminas-math/releases.atom", - "source": "https://github.com/laminas/laminas-math" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-10-18T09:53:37+00:00" - }, - { - "name": "laminas/laminas-oauth", - "version": "2.6.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-oauth.git", - "reference": "7c82c5c0fc5d7bffb5524ca053988455db0e2ac9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-oauth/zipball/7c82c5c0fc5d7bffb5524ca053988455db0e2ac9", - "reference": "7c82c5c0fc5d7bffb5524ca053988455db0e2ac9", - "shasum": "" - }, - "require": { - "laminas/laminas-config": "^3.7", - "laminas/laminas-crypt": "^3.6.0", - "laminas/laminas-http": "^2.15", - "laminas/laminas-i18n": "^2.13.0", - "laminas/laminas-loader": "^2.8", - "laminas/laminas-math": "^3.5", - "laminas/laminas-stdlib": "^3.10", - "laminas/laminas-uri": "^2.9", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" - }, - "conflict": { - "zendframework/zendoauth": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~1.0.0", - "phpunit/phpunit": "^9.5.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\OAuth\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://laminas.dev", - "keywords": [ - "laminas", - "oauth" - ], - "support": { - "chat": "https://laminas.dev/chat", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-oauth/issues", - "rss": "https://github.com/laminas/laminas-oauth/releases.atom", - "source": "https://github.com/laminas/laminas-oauth" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-11-21T14:03:46+00:00" - }, { "name": "laminas/laminas-servicemanager", "version": "3.22.1", @@ -2012,72 +1586,6 @@ ], "time": "2023-10-31T16:26:05+00:00" }, - { - "name": "laminas/laminas-twitter", - "version": "3.7.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-twitter.git", - "reference": "266793e5ac84da4e17749cc97548386bac6bfa15" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-twitter/zipball/266793e5ac84da4e17749cc97548386bac6bfa15", - "reference": "266793e5ac84da4e17749cc97548386bac6bfa15", - "shasum": "" - }, - "require": { - "ext-json": "*", - "laminas/laminas-feed": "^2.7", - "laminas/laminas-http": "^2.5.4", - "laminas/laminas-oauth": "^2.0.3", - "laminas/laminas-stdlib": "^3.0.1", - "laminas/laminas-uri": "^2.5.2", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0" - }, - "conflict": { - "zendframework/zendservice-twitter": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "^2.4", - "phpspec/prophecy-phpunit": "^2.0.1", - "phpunit/phpunit": "^9.5.26", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Twitter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "OOP wrapper for the Twitter web service", - "homepage": "https://laminas.dev", - "keywords": [ - "laminas", - "twitter" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-twitter/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-twitter/issues", - "rss": "https://github.com/laminas/laminas-twitter/releases.atom", - "source": "https://github.com/laminas/laminas-twitter" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "abandoned": true, - "time": "2022-12-05T22:39:00+00:00" - }, { "name": "laminas/laminas-uri", "version": "2.11.0", @@ -7226,12 +6734,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "b4728d9c4af8c60b059c1d7872759eedacccdb12" + "reference": "92ace54210a1b0d6f89afe14f08dda4212854288" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/b4728d9c4af8c60b059c1d7872759eedacccdb12", - "reference": "b4728d9c4af8c60b059c1d7872759eedacccdb12", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/92ace54210a1b0d6f89afe14f08dda4212854288", + "reference": "92ace54210a1b0d6f89afe14f08dda4212854288", "shasum": "" }, "conflict": { @@ -7529,6 +7037,9 @@ "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", + "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2.0.0.0-RC1-dev,<2.0.1", + "microsoft/microsoft-graph-beta": "<2.0.1", + "microsoft/microsoft-graph-core": "<2.0.2", "microweber/microweber": "<=2.0.4", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", @@ -7607,7 +7118,7 @@ "phpmyfaq/phpmyfaq": "<=3.1.7", "phpoffice/phpexcel": "<1.8", "phpoffice/phpspreadsheet": "<1.16", - "phpseclib/phpseclib": "<3.0.34", + "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.34", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.2.5", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5,<5.6.3", @@ -7925,7 +7436,7 @@ "type": "tidelift" } ], - "time": "2023-11-30T20:04:21+00:00" + "time": "2023-12-05T23:04:26+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/autoload/global.php b/config/autoload/global.php index 06d8b1eb..3a1b109f 100644 --- a/config/autoload/global.php +++ b/config/autoload/global.php @@ -48,17 +48,6 @@ 'signing_secret' => getenv('SLACK_SIGNING_SECRET'), 'token' => getenv('SLACK_TOKEN'), ], - 'twitter' => [ - 'access_token' => [ - 'token' => getenv('TWITTER_ACCESS_TOKEN'), - 'secret' => getenv('TWITTER_ACCESS_SECRET'), - ], - 'oauth_options' => [ - 'consumerKey' => getenv('TWITTER_CONSUMER_KEY'), - 'consumerSecret' => getenv('TWITTER_CONSUMER_SECRET'), - ], - 'tweet_verification_token' => getenv('TWEET_VERIFICATION_TOKEN'), - ], 'mastodon' => [ 'name' => getenv('MASTODON_NAME') ?: 'getlaminas', 'instance' => getenv('MASTODON_INSTANCE') ?: 'phpc.social', diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index fe26d75e..dfde630f 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -8,10 +8,8 @@ use Laminas\Diactoros\ResponseFactory; use Laminas\Diactoros\ServerRequestFactory; use Laminas\Diactoros\StreamFactory; -use Laminas\Http\Client\Adapter\Curl; use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\Stratigility\Middleware\ErrorHandler; -use Laminas\Twitter\Twitter as TwitterClient; use Mezzio\Application; use Mezzio\Helper\BodyParams\BodyParamsMiddleware; use Mezzio\MiddlewareFactory; @@ -30,9 +28,6 @@ use Psr\Http\Message\StreamFactoryInterface; use Psr\Log\LoggerInterface; -use const CURLOPT_SSL_VERIFYHOST; -use const CURLOPT_SSL_VERIFYPEER; - class ConfigProvider { public function __invoke(): array @@ -61,24 +56,6 @@ public function __invoke(): array 'signing_secret' => '', 'token' => '', ], - 'twitter' => [ - 'access_token' => [ - 'token' => '', - 'secret' => '', - ], - 'oauth_options' => [ - 'consumerKey' => '', - 'consumerSecret' => '', - ], - 'http_client_options' => [ - 'adapter' => Curl::class, - 'curloptions' => [ - CURLOPT_SSL_VERIFYHOST => false, - CURLOPT_SSL_VERIFYPEER => false, - ], - ], - 'tweet_verification_token' => '', - ], ]; } @@ -105,7 +82,6 @@ public function getDependencies(): array Discourse\ListenerProviderDelegatorFactory::class, GitHub\ListenerProviderDelegatorFactory::class, Slack\ListenerProviderDelegatorFactory::class, - Twitter\ListenerProviderDelegatorFactory::class, ], Discourse\Listener\DiscoursePostListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\DocsBuildActionListener::class => [DeferredServiceListenerDelegator::class], @@ -113,15 +89,10 @@ public function getDependencies(): array GitHub\Listener\GitHubIssueCommentListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\GitHubPullRequestListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\GitHubReleaseSlackListener::class => [DeferredServiceListenerDelegator::class], - GitHub\Listener\GitHubReleaseTweetListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\GitHubReleaseWebsiteUpdateListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\GitHubStatusListener::class => [DeferredServiceListenerDelegator::class], GitHub\Listener\RegisterWebhookListener::class => [DeferredServiceListenerDelegator::class], Slack\Listener\RegenerateAuthorizedUserListListener::class => [DeferredServiceListenerDelegator::class], - Slack\Listener\RetweetListener::class => [DeferredServiceListenerDelegator::class], - Slack\Listener\TweetListener::class => [DeferredServiceListenerDelegator::class], - Slack\Listener\TwitterReplyListener::class => [DeferredServiceListenerDelegator::class], - Twitter\TweetListener::class => [DeferredServiceListenerDelegator::class], ], 'factories' => [ @@ -138,7 +109,6 @@ public function getDependencies(): array GitHub\Listener\GitHubPullRequestListener::class => GitHub\Listener\GitHubPullRequestListenerFactory::class, GitHub\Listener\GitHubReleaseMastodonListener::class => GitHub\Listener\GitHubReleaseMastodonListenerFactory::class, GitHub\Listener\GitHubReleaseSlackListener::class => GitHub\Listener\GitHubReleaseSlackListenerFactory::class, - GitHub\Listener\GitHubReleaseTweetListener::class => GitHub\Listener\GitHubReleaseTweetListenerFactory::class, GitHub\Listener\GitHubReleaseWebsiteUpdateListener::class => GitHub\Listener\GitHubReleaseWebsiteUpdateListenerFactory::class, GitHub\Listener\GitHubStatusListener::class => GitHub\Listener\GitHubStatusListenerFactory::class, GitHub\Listener\RegisterWebhookListener::class => GitHub\Listener\RegisterWebhookListenerFactory::class, @@ -156,9 +126,6 @@ public function getDependencies(): array ResponseFactory::class => InvokableFactory::class, ServerRequestFactory::class => InvokableFactory::class, Slack\Listener\RegenerateAuthorizedUserListListener::class => Slack\Listener\RegenerateAuthorizedUserListListenerFactory::class, - Slack\Listener\RetweetListener::class => Slack\Listener\RetweetListenerFactory::class, - Slack\Listener\TweetListener::class => Slack\Listener\TweetListenerFactory::class, - Slack\Listener\TwitterReplyListener::class => Slack\Listener\TwitterReplyListenerFactory::class, Slack\Middleware\VerificationMiddleware::class => Slack\Middleware\VerificationMiddlewareFactory::class, Slack\Middleware\SlashCommandHandler::class => Slack\Middleware\SlashCommandHandlerFactory::class, Slack\SlackClientInterface::class => Slack\SlackClientFactory::class, @@ -166,17 +133,10 @@ public function getDependencies(): array Slack\SlashCommand\BuildDocsCommand::class => Slack\SlashCommand\BuildDocsCommandFactory::class, Slack\SlashCommand\RegenerateAuthorizedUserListCommand::class => Slack\SlashCommand\RegenerateAuthorizedUserListCommandFactory::class, Slack\SlashCommand\RegisterRepoCommand::class => Slack\SlashCommand\RegisterRepoCommandFactory::class, - Slack\SlashCommand\RetweetCommand::class => Slack\SlashCommand\RetweetCommandFactory::class, Slack\SlashCommand\SlashCommandResponseFactory::class => Slack\SlashCommand\SlashCommandResponseFactoryFactory::class, Slack\SlashCommand\SlashCommands::class => Slack\SlashCommand\SlashCommandsFactory::class, - Slack\SlashCommand\TweetCommand::class => Slack\SlashCommand\TweetCommandFactory::class, - Slack\SlashCommand\TwitterReplyCommand::class => Slack\SlashCommand\TwitterReplyCommandFactory::class, - Twitter\TweetHandler::class => Twitter\TweetHandlerFactory::class, - Twitter\TweetListener::class => Twitter\TweetListenerFactory::class, - Twitter\VerificationMiddleware::class => Twitter\VerificationMiddlewareFactory::class, StreamFactory::class => InvokableFactory::class, SwooleLoggerFactory::SWOOLE_LOGGER => Factory\AccessLogFactory::class, - TwitterClient::class => Factory\TwitterClientFactory::class, UrlHelper::class => Factory\UrlHelperFactory::class, ], ]; @@ -232,13 +192,5 @@ public function registerRoutes( Slack\Middleware\VerificationMiddleware::class, Slack\Middleware\SlashCommandHandler::class, ], 'api.slack'); - - $app->post('/api/twitter/{token:[^/]+}', [ - $initialMiddleware, - ProblemDetailsMiddleware::class, - BodyParamsMiddleware::class, - Twitter\VerificationMiddleware::class, - Twitter\TweetHandler::class, - ], 'api.twitter'); } } diff --git a/src/Factory/TwitterClientFactory.php b/src/Factory/TwitterClientFactory.php deleted file mode 100644 index f0e5dcac..00000000 --- a/src/Factory/TwitterClientFactory.php +++ /dev/null @@ -1,17 +0,0 @@ -get('config')['twitter']; - return new Twitter($config); - } -} diff --git a/src/GitHub/Listener/GitHubReleaseTweetListener.php b/src/GitHub/Listener/GitHubReleaseTweetListener.php deleted file mode 100644 index 2d3e7d3e..00000000 --- a/src/GitHub/Listener/GitHubReleaseTweetListener.php +++ /dev/null @@ -1,70 +0,0 @@ -twitter = $twitterClient; - $this->logger = $logger; - } - - public function __invoke(GitHubRelease $message): void - { - if (! $message->isPublished()) { - return; - } - - if (! $this->prepareTwitterClient()) { - $this->logger->error(sprintf( - 'Could not validate twitter credentials; did not tweet release %s %s', - $message->getPackage(), - $message->getVersion() - )); - return; - } - - $tweet = str_replace( - ['{package}', '{version}', '{url}'], - [$message->getPackage(), $message->getVersion(), $message->getUrl()], - self::TWEET_TEMPLATE - ); - - $response = $this->twitter->statusesUpdate($tweet); - if ($response->isError()) { - $this->logger->error(sprintf( - 'Error tweeting release %s %s: %s', - $message->getPackage(), - $message->getVersion(), - implode("\n", $response->getErrors()) - )); - } - } - - private function prepareTwitterClient(): bool - { - $response = $this->twitter->accountVerifyCredentials(); - return $response->isSuccess(); - } -} diff --git a/src/GitHub/Listener/GitHubReleaseTweetListenerFactory.php b/src/GitHub/Listener/GitHubReleaseTweetListenerFactory.php deleted file mode 100644 index 97d24914..00000000 --- a/src/GitHub/Listener/GitHubReleaseTweetListenerFactory.php +++ /dev/null @@ -1,20 +0,0 @@ -get(Twitter::class), - $container->get(LoggerInterface::class) - ); - } -} diff --git a/src/GitHub/ListenerProviderDelegatorFactory.php b/src/GitHub/ListenerProviderDelegatorFactory.php index 62c1c48a..d7bc12f7 100644 --- a/src/GitHub/ListenerProviderDelegatorFactory.php +++ b/src/GitHub/ListenerProviderDelegatorFactory.php @@ -21,7 +21,6 @@ public function __invoke(ContainerInterface $container, string $name, callable $ $provider->listen(Event\GitHubPullRequest::class, $container->get(Listener\GitHubPullRequestListener::class)); $provider->listen(Event\GitHubRelease::class, $container->get(Listener\GitHubReleaseSlackListener::class)); $provider->listen(Event\GitHubRelease::class, $container->get(Listener\GitHubReleaseWebsiteUpdateListener::class)); - $provider->listen(Event\GitHubRelease::class, $container->get(Listener\GitHubReleaseTweetListener::class)); $provider->listen(Event\GitHubRelease::class, $container->get(Listener\GitHubReleaseMastodonListener::class)); $provider->listen(Event\GitHubStatus::class, $container->get(Listener\GitHubStatusListener::class)); $provider->listen(Event\RegisterWebhook::class, $container->get(Listener\RegisterWebhookListener::class)); diff --git a/src/Slack/Event/Retweet.php b/src/Slack/Event/Retweet.php deleted file mode 100644 index 29ab9043..00000000 --- a/src/Slack/Event/Retweet.php +++ /dev/null @@ -1,30 +0,0 @@ -original = $original; - $this->responseUrl = $responseUrl; - } - - public function original(): string - { - return $this->original; - } - - public function responseUrl(): string - { - return $this->responseUrl; - } -} diff --git a/src/Slack/Event/Tweet.php b/src/Slack/Event/Tweet.php deleted file mode 100644 index 9c554a76..00000000 --- a/src/Slack/Event/Tweet.php +++ /dev/null @@ -1,39 +0,0 @@ -message = $message; - $this->media = $media; - $this->responseUrl = $responseUrl; - } - - public function message(): string - { - return $this->message; - } - - public function media(): ?string - { - return $this->media; - } - - public function responseUrl(): string - { - return $this->responseUrl; - } -} diff --git a/src/Slack/Event/TwitterReply.php b/src/Slack/Event/TwitterReply.php deleted file mode 100644 index 2d13ad63..00000000 --- a/src/Slack/Event/TwitterReply.php +++ /dev/null @@ -1,42 +0,0 @@ -replyUrl = $replyUrl; - $this->message = $message; - $this->responseUrl = $responseUrl; - } - - public function message(): string - { - return $this->message; - } - - public function replyUrl(): string - { - return $this->replyUrl; - } - - public function responseUrl(): string - { - return $this->responseUrl; - } -} diff --git a/src/Slack/Listener/RetweetListener.php b/src/Slack/Listener/RetweetListener.php deleted file mode 100644 index 0e2bd288..00000000 --- a/src/Slack/Listener/RetweetListener.php +++ /dev/null @@ -1,56 +0,0 @@ -twitter = $twitter; - $this->slack = $slack; - } - - public function __invoke(Retweet $retweet): void - { - $original = $retweet->original(); - $id = substr($original, strrpos($original, '/') + 1); - - try { - $this->twitter->post(sprintf('statuses/retweet/%s', $id)); - } catch (Throwable $e) { - $this->report(sprintf( - '*ERROR* Unable to retweet %s: %s', - $original, - $e->getMessage() - ), $retweet->responseUrl()); - return; - } - - $this->report('Retweet sent', $retweet->responseUrl()); - } - - private function report(string $message, string $responseUrl): void - { - $response = new SlashResponseMessage(); - $response->setText($message); - $this->slack->sendWebhookMessage($responseUrl, $response); - } -} diff --git a/src/Slack/Listener/RetweetListenerFactory.php b/src/Slack/Listener/RetweetListenerFactory.php deleted file mode 100644 index 7e0808cf..00000000 --- a/src/Slack/Listener/RetweetListenerFactory.php +++ /dev/null @@ -1,20 +0,0 @@ -get(Twitter::class), - $container->get(SlackClientInterface::class) - ); - } -} diff --git a/src/Slack/Listener/TweetListener.php b/src/Slack/Listener/TweetListener.php deleted file mode 100644 index e40f08da..00000000 --- a/src/Slack/Listener/TweetListener.php +++ /dev/null @@ -1,187 +0,0 @@ -twitter = $twitter; - $this->slack = $slack; - $this->http = $http; - $this->imageFactory = function (string $filename, string $mediaType) { - return new Image($filename, $mediaType); - }; - } - - public function __invoke(Tweet $tweet): void - { - if ($tweet->media()) { - $this->sendTweetWithMedia($tweet); - return; - } - - try { - $this->twitter->statusesUpdate($tweet->message()); - } catch (Throwable $e) { - $this->reportError( - sprintf('*ERROR* sending tweet: %s', $e->getMessage()), - $tweet - ); - return; - } - - $this->reportSuccess($tweet); - } - - public function sendTweetWithMedia(Tweet $tweet): void - { - $mediaId = $this->uploadMedia($tweet); - if (null === $mediaId) { - return; - } - - try { - $this->twitter->statusesUpdate( - $tweet->message(), - null, - ['media_ids' => [$mediaId]] - ); - } catch (Throwable $e) { - $this->reportError( - sprintf('*ERROR* sending tweet: %s', $e->getMessage()), - $tweet - ); - return; - } - - $this->reportSuccess($tweet); - } - - private function reportSuccess(Tweet $tweet): void - { - $message = new SlashResponseMessage(); - $message->setText(sprintf( - 'Tweet sent with message: %s', - $tweet->message() - )); - $this->slack->sendWebhookMessage($tweet->responseUrl(), $message); - } - - private function reportError(string $error, Tweet $tweet): void - { - $message = new SlashResponseMessage(); - $message->setText($error); - $this->slack->sendWebhookMessage($tweet->responseUrl(), $message); - } - - private function uploadMedia(Tweet $tweet): ?string - { - $url = $tweet->media(); - $type = $this->determineImageType($tweet); - if (null === $type) { - return null; - } - - $filename = $this->fetchImage($tweet); - if (null === $filename) { - return null; - } - - $image = ($this->imageFactory)($filename, $type); - try { - $response = $image->upload($this->twitter->getHttpClient()); - } catch (Throwable $e) { - $this->reportError(sprintf( - '*ERROR* Unable to upload media from %s: %s', - $url, - $e->getMessage() - ), $tweet); - return null; - } - - return (string) $response->media_id; - } - - private function determineImageType(Tweet $tweet): ?string - { - $request = $this->http->createRequest('HEAD', $tweet->media()); - $response = $this->http->send($request); - $contentType = $response->getHeaderLine('Content-Type'); - if (! in_array(strtolower($contentType), self::ALLOWED_IMAGE_TYPES, true)) { - $this->reportError(sprintf( - '*ERROR* Media is of content type "%s"; must be one of %s', - $contentType, - implode(', ', self::ALLOWED_IMAGE_TYPES) - ), $tweet); - return null; - } - return $contentType; - } - - private function fetchImage(Tweet $tweet): ?string - { - $url = $tweet->media(); - $request = $this->http->createRequest('GET', $tweet->media()); - $response = $this->http->send($request); - - if (200 !== $response->getStatusCode()) { - $this->reportError(sprintf('*ERROR* Unable to fetch media from %s', $url), $tweet); - return null; - } - - $filename = sprintf('data/cache/%s', basename($url)); - ($this->filePopulator)($filename, (string) $response->getBody()); - - return $filename; - } -} diff --git a/src/Slack/Listener/TweetListenerFactory.php b/src/Slack/Listener/TweetListenerFactory.php deleted file mode 100644 index dd1f8fb4..00000000 --- a/src/Slack/Listener/TweetListenerFactory.php +++ /dev/null @@ -1,22 +0,0 @@ -get(Twitter::class), - $container->get(SlackClientInterface::class), - $container->get(HttpClientInterface::class) - ); - } -} diff --git a/src/Slack/Listener/TwitterReplyListener.php b/src/Slack/Listener/TwitterReplyListener.php deleted file mode 100644 index bb6e15b1..00000000 --- a/src/Slack/Listener/TwitterReplyListener.php +++ /dev/null @@ -1,83 +0,0 @@ -twitter = $twitter; - $this->slack = $slack; - } - - public function __invoke(TwitterReply $twitterReply): void - { - $replyUrl = $twitterReply->replyUrl(); - try { - $replyTo = $this->discoverReplyUsername($replyUrl); - $this->twitter->post('statuses/update', [ - 'status' => sprintf('@%s %s', $replyTo, $twitterReply->message()), - 'in_reply_to_status_id' => $this->getReplyId($replyUrl), - 'username' => sprintf('@%s', $replyTo), - ]); - } catch (Throwable $e) { - $this->notifySlack(sprintf( - '*ERROR* Failed to send Twitter reply to %s', - $replyUrl - ), $twitterReply->responseUrl()); - return; - } - - $this->notifySlack(sprintf( - 'Twitter reply sent to %s', - $replyUrl - ), $twitterReply->responseUrl()); - } - - /** @throws RuntimeException */ - private function discoverReplyUsername(string $url): string - { - $matches = []; - if (! preg_match('#^https?://twitter.com/(?P[^/]+)/status/[^/]+$#', $url, $matches)) { - throw new RuntimeException('Invalid reply-to URL provided!'); - } - return $matches['user']; - } - - private function getReplyId(string $url): string - { - $path = parse_url($url, PHP_URL_PATH); - $segments = explode('/', $path); - return array_pop($segments); - } - - private function notifySlack(string $text, string $responseUrl): void - { - $message = new SlashResponseMessage(); - $message->setText($text); - $this->slack->sendWebhookMessage($responseUrl, $message); - } -} diff --git a/src/Slack/Listener/TwitterReplyListenerFactory.php b/src/Slack/Listener/TwitterReplyListenerFactory.php deleted file mode 100644 index 72d251da..00000000 --- a/src/Slack/Listener/TwitterReplyListenerFactory.php +++ /dev/null @@ -1,20 +0,0 @@ -get(Twitter::class), - $container->get(SlackClientInterface::class) - ); - } -} diff --git a/src/Slack/ListenerProviderDelegatorFactory.php b/src/Slack/ListenerProviderDelegatorFactory.php index a7f518e3..a9094471 100644 --- a/src/Slack/ListenerProviderDelegatorFactory.php +++ b/src/Slack/ListenerProviderDelegatorFactory.php @@ -17,9 +17,7 @@ public function __invoke(ContainerInterface $container, string $name, callable $ Event\RegenerateAuthorizedUserList::class, $container->get(Listener\RegenerateAuthorizedUserListListener::class) ); - $provider->listen(Event\Retweet::class, $container->get(Listener\RetweetListener::class)); - $provider->listen(Event\Tweet::class, $container->get(Listener\TweetListener::class)); - $provider->listen(Event\TwitterReply::class, $container->get(Listener\TwitterReplyListener::class)); + return $provider; } } diff --git a/src/Slack/SlashCommand/RetweetCommand.php b/src/Slack/SlashCommand/RetweetCommand.php deleted file mode 100644 index 7d7b2392..00000000 --- a/src/Slack/SlashCommand/RetweetCommand.php +++ /dev/null @@ -1,76 +0,0 @@ -responseFactory = $responseFactory; - $this->dispatcher = $dispatcher; - } - - public function command(): string - { - return 'retweet'; - } - - public function usage(): string - { - return '[url]'; - } - - public function help(): string - { - return 'Retweet an existing tweet, referenced by the url, via the getlaminas account.'; - } - - public function validate( - SlashCommandRequest $request, - AuthorizedUserListInterface $authorizedUsers - ): ?ResponseInterface { - if (! $authorizedUsers->isAuthorized($request->userId())) { - return $this->responseFactory->createUnauthorizedResponse(); - } - - $url = trim($request->text()); - - try { - Assert::that($url)->url()->regex('#^https://twitter.com/[^/]+/status/[^/]+$#'); - } catch (AssertionFailedException $e) { - return $this->responseFactory->createResponse( - 'Retweet URL MUST be a valid URL pointing to a tweet.' - ); - } - - return null; - } - - public function dispatch(SlashCommandRequest $request): ?ResponseInterface - { - $text = $request->text(); - $this->dispatcher->dispatch(new Retweet( - trim($request->text()), - $request->responseUrl() - )); - return null; - } -} diff --git a/src/Slack/SlashCommand/RetweetCommandFactory.php b/src/Slack/SlashCommand/RetweetCommandFactory.php deleted file mode 100644 index 6ba42ec9..00000000 --- a/src/Slack/SlashCommand/RetweetCommandFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -get(SlashCommandResponseFactory::class), - $container->get(EventDispatcherInterface::class) - ); - } -} diff --git a/src/Slack/SlashCommand/SlashCommandsFactory.php b/src/Slack/SlashCommand/SlashCommandsFactory.php index 50d36a83..f3ed9696 100644 --- a/src/Slack/SlashCommand/SlashCommandsFactory.php +++ b/src/Slack/SlashCommand/SlashCommandsFactory.php @@ -19,9 +19,6 @@ public function __invoke(ContainerInterface $container): SlashCommands $commands->attach($container->get(BuildDocsCommand::class)); $commands->attach($container->get(RegenerateAuthorizedUserListCommand::class)); $commands->attach($container->get(RegisterRepoCommand::class)); - $commands->attach($container->get(RetweetCommand::class)); - $commands->attach($container->get(TweetCommand::class)); - $commands->attach($container->get(TwitterReplyCommand::class)); return $commands; } diff --git a/src/Slack/SlashCommand/TweetCommand.php b/src/Slack/SlashCommand/TweetCommand.php deleted file mode 100644 index 72506ccb..00000000 --- a/src/Slack/SlashCommand/TweetCommand.php +++ /dev/null @@ -1,109 +0,0 @@ -responseFactory = $responseFactory; - $this->dispatcher = $dispatcher; - } - - public function command(): string - { - return 'tweet'; - } - - public function usage(): string - { - return '[media:url] message'; - } - - public function help(): string - { - return 'Tweet the [message] via the getlaminas account.' - . ' You can optionally provide media to embed using a "media:url" pair' - . ' (no brackets required).'; - } - - public function validate( - SlashCommandRequest $request, - AuthorizedUserListInterface $authorizedUsers - ): ?ResponseInterface { - if (! $authorizedUsers->isAuthorized($request->userId())) { - return $this->responseFactory->createUnauthorizedResponse(); - } - - $text = $request->text(); - $message = $this->normalizeMessage($text); - $length = strlen($message); - if ($length > 280 || $length === 0) { - return $this->responseFactory->createResponse( - 'Message MUST be at least 1 and no more than 280 characters.' - ); - } - - $media = $this->discoverMedia($text); - if (null === $media) { - return null; - } - - try { - Assert::that($media)->url(); - } catch (AssertionFailedException $e) { - return $this->responseFactory->createResponse( - 'Media value MUST be a valid URL.' - ); - } - - return null; - } - - public function dispatch(SlashCommandRequest $request): ?ResponseInterface - { - $text = $request->text(); - $this->dispatcher->dispatch(new Tweet( - $this->normalizeMessage($text), - $this->discoverMedia($text), - $request->responseUrl() - )); - return null; - } - - private function normalizeMessage(string $text): string - { - $text = preg_replace('#media:\S+#', '', $text); - return trim($text); - } - - private function discoverMedia(string $text): ?string - { - $matches = []; - if (! preg_match('#media:(?P\S+)#', $text, $matches)) { - return null; - } - return $matches['url']; - } -} diff --git a/src/Slack/SlashCommand/TweetCommandFactory.php b/src/Slack/SlashCommand/TweetCommandFactory.php deleted file mode 100644 index 262d128f..00000000 --- a/src/Slack/SlashCommand/TweetCommandFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -get(SlashCommandResponseFactory::class), - $container->get(EventDispatcherInterface::class) - ); - } -} diff --git a/src/Slack/SlashCommand/TwitterReplyCommand.php b/src/Slack/SlashCommand/TwitterReplyCommand.php deleted file mode 100644 index 84c749ef..00000000 --- a/src/Slack/SlashCommand/TwitterReplyCommand.php +++ /dev/null @@ -1,109 +0,0 @@ -responseFactory = $responseFactory; - $this->dispatcher = $dispatcher; - } - - public function command(): string - { - return 'reply-to-tweet'; - } - - public function usage(): string - { - return '{url} {message}'; - } - - public function help(): string - { - return 'Reply to the tweet represented by {url} via the getlaminas account.'; - } - - public function validate( - SlashCommandRequest $request, - AuthorizedUserListInterface $authorizedUsers - ): ?ResponseInterface { - if (! $authorizedUsers->isAuthorized($request->userId())) { - return $this->responseFactory->createUnauthorizedResponse(); - } - - $text = trim($request->text()); - $url = $this->parseReplyUrlFromText($text); - $message = $this->parseMessageFromText($text); - - try { - Assert::that($url)->url()->regex('#^https://twitter.com/[^/]+/status/[^/]+$#'); - } catch (AssertionFailedException $e) { - return $this->responseFactory->createResponse( - 'Reply URL MUST be a valid URL pointing to a tweet.' - ); - } - - $length = strlen($message); - if ($length > 280 || $length === 0) { - return $this->responseFactory->createResponse( - 'Message MUST be at least 1 and no more than 280 characters.' - ); - } - - return null; - } - - public function dispatch(SlashCommandRequest $request): ?ResponseInterface - { - $text = trim($request->text()); - $this->dispatcher->dispatch(new TwitterReply( - $this->parseReplyUrlFromText($text), - $this->parseMessageFromText($text), - $request->responseUrl() - )); - return null; - } - - private function parseMessageFromText(string $text): string - { - if (! preg_match('/\s/', $text)) { - return ''; - } - - [$url, $message] = explode(' ', $text, 2); - return $message; - } - - private function parseReplyUrlFromText(string $text): ?string - { - if (! preg_match('/\s/', $text)) { - return $text; - } - - [$url, $message] = explode(' ', $text, 2); - return $url; - } -} diff --git a/src/Slack/SlashCommand/TwitterReplyCommandFactory.php b/src/Slack/SlashCommand/TwitterReplyCommandFactory.php deleted file mode 100644 index aeb418d6..00000000 --- a/src/Slack/SlashCommand/TwitterReplyCommandFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -get(SlashCommandResponseFactory::class), - $container->get(EventDispatcherInterface::class) - ); - } -} diff --git a/src/Twitter/ListenerProviderDelegatorFactory.php b/src/Twitter/ListenerProviderDelegatorFactory.php deleted file mode 100644 index d26c4c82..00000000 --- a/src/Twitter/ListenerProviderDelegatorFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -listen(Tweet::class, $container->get(TweetListener::class)); - return $provider; - } -} diff --git a/src/Twitter/Tweet.php b/src/Twitter/Tweet.php deleted file mode 100644 index 2a02f687..00000000 --- a/src/Twitter/Tweet.php +++ /dev/null @@ -1,44 +0,0 @@ -message = $message; - $this->url = $url; - $this->timestamp = $timestamp; - } - - public function message(): string - { - return $this->message; - } - - public function timestamp(): DateTimeInterface - { - return $this->timestamp; - } - - public function url(): string - { - return $this->url; - } -} diff --git a/src/Twitter/TweetHandler.php b/src/Twitter/TweetHandler.php deleted file mode 100644 index 713b1dac..00000000 --- a/src/Twitter/TweetHandler.php +++ /dev/null @@ -1,60 +0,0 @@ -responseFactory = $responseFactory; - $this->dispatcher = $dispatcher; - } - - public function handle(ServerRequestInterface $request): ResponseInterface - { - $params = $request->getParsedBody(); - - $this->dispatcher->dispatch(new Tweet( - $params['text'], - $params['url'], - $this->createDateTimeFromString($params['timestamp']) - )); - - return $this->responseFactory->createResponse(202); - } - - private function createDateTimeFromString(string $timestamp): DateTimeInterface - { - $matches = []; - if (! preg_match('/^(?P.*?)\s+at\s+(?P