-
Notifications
You must be signed in to change notification settings - Fork 1
Initial PR to send scheduled reports to a Slack channel, #PG-4491 #1
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
Changes from 13 commits
5e707e7
edadd47
6e8dd35
699a841
30720e9
d3629e1
1b674ec
0158fca
99c5115
70e3172
ed18883
2349db0
8bec9b9
862733f
67c420f
f5b5c2a
3cf03a3
5e47178
c74d2d2
35cd206
ebbbc48
37fd322
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # Action for running tests | ||
| # This file has been automatically created. | ||
| # To recreate it you can run this command | ||
| # ./console generate:test-action --plugin="Slack" --php-versions="7.2,8.4" --schedule-cron="0 5 * * 6" | ||
|
|
||
| name: Plugin Slack Tests | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize] | ||
| push: | ||
| branches: | ||
| - '**.x-dev' | ||
| workflow_dispatch: | ||
| schedule: | ||
| - cron: "0 5 * * 6" | ||
|
|
||
| permissions: | ||
| actions: read | ||
| checks: none | ||
| contents: read | ||
| deployments: none | ||
| issues: read | ||
| packages: none | ||
| pull-requests: read | ||
| repository-projects: none | ||
| security-events: none | ||
| statuses: none | ||
|
|
||
| concurrency: | ||
| group: php-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| PluginTests: | ||
| runs-on: ubuntu-24.04 | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| php: [ '7.2', '8.4' ] | ||
| target: ['minimum_required_matomo', 'maximum_supported_matomo'] | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| lfs: true | ||
| persist-credentials: false | ||
| - name: Install package ripgrep | ||
| run: sudo apt-get install ripgrep | ||
| - name: Run tests | ||
| uses: matomo-org/github-action-tests@main | ||
| with: | ||
| plugin-name: 'Slack' | ||
| php-version: ${{ matrix.php }} | ||
| test-type: 'PluginTests' | ||
| matomo-test-branch: ${{ matrix.target }} | ||
| redis-service: true | ||
| artifacts-pass: ${{ secrets.ARTIFACTS_PASS }} | ||
| upload-artifacts: ${{ matrix.php == '7.2' && matrix.target == 'maximum_supported_matomo' }} | ||
| UI: | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| lfs: true | ||
| persist-credentials: false | ||
| - name: running tests | ||
| uses: matomo-org/github-action-tests@main | ||
| with: | ||
| plugin-name: 'Slack' | ||
| matomo-test-branch: 'maximum_supported_matomo' | ||
| test-type: 'UI' | ||
| php-version: '7.2' | ||
| node-version: '16' | ||
| redis-service: true | ||
| artifacts-pass: ${{ secrets.ARTIFACTS_PASS }} | ||
| upload-artifacts: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| name: PHPCS check | ||
|
|
||
| on: pull_request | ||
|
|
||
| permissions: | ||
| actions: read | ||
| checks: read | ||
| contents: read | ||
| deployments: none | ||
| issues: read | ||
| packages: none | ||
| pull-requests: read | ||
| repository-projects: none | ||
| security-events: none | ||
| statuses: read | ||
|
|
||
| jobs: | ||
| phpcs: | ||
| name: PHPCS | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| lfs: false | ||
| persist-credentials: false | ||
| - name: Setup PHP | ||
| uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: '7.4' | ||
| tools: cs2pr | ||
| - name: Install dependencies | ||
| run: | ||
| composer init --name=matomo/slack --quiet; | ||
| composer --no-plugins config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true -n; | ||
| composer config repositories.matomo-coding-standards vcs https://github.com/matomo-org/matomo-coding-standards -n; | ||
| composer require matomo-org/matomo-coding-standards:dev-master; | ||
| composer install --dev --prefer-dist --no-progress --no-suggest | ||
| - name: Check PHP code styles | ||
| id: phpcs | ||
| run: ./vendor/bin/phpcs --report-full --standard=phpcs.xml --report-checkstyle=./phpcs-report.xml | ||
| - name: Show PHPCS results in PR | ||
| if: ${{ always() && steps.phpcs.outcome == 'failure' }} | ||
| run: cs2pr ./phpcs-report.xml --prepend-filename | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| /tests/System/processed/ | ||
| /tests/Integration/Importers/processed/ | ||
| /tests/UI/processed-ui-screenshots/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| ## Changelog | ||
|
|
||
| 5.0.0 - 2025-09-01 | ||
| - Initial release to send scheduled reports to a Slack channel |
AltamashShaikh marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * Matomo - free/libre analytics platform | ||
| * | ||
| * @link https://matomo.org | ||
| * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later | ||
| */ | ||
|
|
||
AltamashShaikh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| namespace Piwik\Plugins\Slack; | ||
|
|
||
| use Piwik\Container\StaticContainer; | ||
| use Piwik\Http; | ||
| use Piwik\Log\LoggerInterface; | ||
|
|
||
| class ScheduleReportSlack | ||
| { | ||
| private $subject; | ||
| private $filename; | ||
|
|
||
| private $fileContents; | ||
|
|
||
| private $channel; | ||
|
|
||
| private $fileID; | ||
|
|
||
| private $token; | ||
AltamashShaikh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private const SLACK_UPLOAD_URL_EXTERNAL = 'https://slack.com/api/files.getUploadURLExternal'; | ||
| private const SLACK_COMPLETE_UPLOAD_EXTERNAL = 'https://slack.com/api/files.completeUploadExternal'; | ||
|
|
||
| private const SLACK_TIMEOUT = 5000; | ||
|
|
||
| private $logger; | ||
AltamashShaikh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| public function send( | ||
| string $subject, | ||
| string $fileName, | ||
| string $fileContents, | ||
| string $channel, | ||
| #[\SensitiveParameter] | ||
| string $token | ||
AltamashShaikh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) { | ||
AltamashShaikh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $this->subject = $subject; | ||
| $this->filename = $fileName; | ||
| $this->fileContents = $fileContents; | ||
| $this->channel = $channel; | ||
| $this->token = $token; | ||
| $this->logger = StaticContainer::get(LoggerInterface::class); | ||
|
|
||
| $uploadURL = $this->getUploadURLExternal(); | ||
| if (!empty($uploadURL) && $this->sendFile($uploadURL)) { | ||
| return $this->completeUploadExternal(); | ||
| } | ||
|
|
||
| $this->logger->debug('Unable to send ' . $fileName . ' report to Slack'); | ||
AltamashShaikh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return false; | ||
| } | ||
|
|
||
| public function getUploadURLExternal(): string | ||
| { | ||
| try { | ||
| $response = $this->sendHttpRequest( | ||
| self::SLACK_UPLOAD_URL_EXTERNAL, | ||
| self::SLACK_TIMEOUT, | ||
| [ | ||
| 'token' => $this->token, | ||
| 'filename' => $this->filename, | ||
| 'length' => strlen($this->fileContents), | ||
| ], | ||
| ['Content-Type' => 'multipart/form-data'] | ||
| ); | ||
| } catch (\Exception $e) { | ||
| $this->logger->debug($e->getMessage()); | ||
| return ''; | ||
| } | ||
AltamashShaikh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| $data = json_decode($response, true); | ||
| if (!empty($data) && !empty($data['upload_url']) && !empty($data['file_id'])) { | ||
| $this->fileID = $data['file_id']; | ||
|
|
||
| return $data['upload_url']; | ||
| } | ||
|
|
||
| return ''; | ||
| } | ||
|
|
||
| public function sendFile(string $uploadURL): bool | ||
| { | ||
| try { | ||
| $response = $this->sendHttpRequest( | ||
| $uploadURL, | ||
| self::SLACK_TIMEOUT, | ||
| [$this->fileContents], | ||
| [], | ||
| true | ||
| ); | ||
| } catch (\Exception $e) { | ||
| $this->logger->debug($e->getMessage()); | ||
| return false; | ||
| } | ||
|
|
||
| return stripos($response, 'OK') !== false; | ||
|
||
| } | ||
|
|
||
| public function completeUploadExternal(): bool | ||
| { | ||
| try { | ||
| $response = $this->sendHttpRequest( | ||
| self::SLACK_COMPLETE_UPLOAD_EXTERNAL, | ||
| self::SLACK_TIMEOUT, | ||
| [ | ||
| 'token' => $this->token, | ||
| 'files' => json_encode([['id' => $this->fileID]]), | ||
| 'channel_id' => $this->channel, | ||
| 'initial_comment' => $this->subject | ||
| ], | ||
| ['Content-Type' => 'multipart/form-data'] | ||
| ); | ||
| } catch (\Exception $e) { | ||
| $this->logger->debug($e->getMessage()); | ||
| return false; | ||
| } | ||
|
|
||
| $data = json_decode($response, true); | ||
|
|
||
| return !empty($data['ok']); | ||
| } | ||
|
|
||
| private function sendHttpRequest(string $url, int $timeout, array $requestBody, array $additionalHeaders = [], $requestBodyAsString = false) | ||
| { | ||
| if ($requestBodyAsString && !empty($requestBody[0])) { | ||
| $requestBody = $requestBody[0]; | ||
| } | ||
AltamashShaikh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return Http::sendHttpRequestBy( | ||
| Http::getTransportMethod(), | ||
| $url, | ||
| $timeout, | ||
| $userAgent = null, | ||
| $destinationPath = null, | ||
| $file = null, | ||
| $followDepth = 0, | ||
| $acceptLanguage = false, | ||
| $acceptInvalidSslCertificate = false, | ||
| $byteRange = false, | ||
| $getExtendedInfo = false, | ||
| $httpMethod = 'POST', | ||
| $httpUsername = null, | ||
| $httpPassword = null, | ||
| $requestBody, | ||
| $additionalHeaders | ||
| ); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.