diff --git a/.editorconfig b/.editorconfig index 1181d82..7edee29 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,8 +7,11 @@ root = true [*] end_of_line = lf insert_final_newline = true +indent_style = space # 4 space indentation [*.php] -indent_style = space indent_size = 4 + +[*.yml] +indent_size = 2 diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml new file mode 100644 index 0000000..5c6b240 --- /dev/null +++ b/.github/workflows/phpcs.yml @@ -0,0 +1,37 @@ +name: PHP CS Checks + +on: [ push ] + +jobs: + phpcs-check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: simplexml + coverage: xdebug + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run PHPCS Check + run: | + composer install --prefer-dist --no-progress + composer require staabm/annotate-pull-request-from-checkstyle + ./vendor/bin/phpcs -q --report=checkstyle | vendor/bin/cs2pr --graceful-warnings diff --git a/.gitignore b/.gitignore index 7478236..b889f78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ config.php .DS_Store -.idea \ No newline at end of file +.idea +last.txt +phpcs.xml +vendor diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a545bb9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres +to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2021-11-12 + +- Initial real release, of a 23 Aug 2010 started project. +- Added PHP_CodeSniffer +- Refactored the codebase for fun +- Added this changelog diff --git a/README.md b/README.md index 462b7f4..4a244b9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# xkcd-Mailer # +# xkcd-Mailer -Takes the first/latest item from the [xkcd](http://xkcd.com/) atom-feed and mails the image and punchline to a specified email address, if it has not been sent before. +Takes the first/latest item from the [xkcd](http://xkcd.com/) atom-feed +and mails the image and punchline to a specified email address, +if it has not been sent before. -## configuration ## +## configuration The script needs a simple configuration. Modify ``config.example.php`` to fit your needs and save as ``config.php`` @@ -14,7 +16,7 @@ The script needs a simple configuration. Modify ``config.example.php`` to fit yo */ // Your timezone, PHP5 required. -// See full list: http://www.php.net/manual/en/timezones.php +// See full list: https://www.php.net/manual/en/timezones.php date_default_timezone_set("Europe/Helsinki"); // Your destination @@ -25,31 +27,33 @@ $from = "xkcd mailer "; $lastfile = "last.txt"; ``` - -## crontab example ## +## crontab example Run every hour. 0 * * * * /usr/bin/php /full/path/to/xkcd-mailer.php -This version will check if the last post was already emailed and will only send the post -if it has not been emailed yet. +This version will check if the last post was already emailed and will only +send the post if it has not been emailed yet. -## caveats ## +## caveats Make sure to set $lastfile to a path that you have write access to. -## changes ## +## changes - 2014-03-30 @wojas added check to see if a post already sent -## contributing ## +## contributing - Fork the code - Do your changes - Send pull request - Bask in glory of open source love -## forks <3 ## +## forks <3 -* [raamdev/pennyarcade-Mailer](https://github.com/raamdev/pennyarcade-Mailer) - [Penny Arcade](http://penny-arcade.com/comic) emailer by Raam Dev +* [raamdev/pennyarcade-Mailer][pa-git] - [Penny Arcade][pa-url] emailer by Raam Dev + +[pa-git]: https://github.com/raamdev/pennyarcade-Mailer +[pa-url]: http://penny-arcade.com/comic diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..fb12842 --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "ivuorinen/xkcd-mailer", + "description": "XKCD Mailer", + "minimum-stability": "stable", + "license": "MIT", + "authors": [ + { + "name": "Ismo Vuorinen", + "homepage": "https://github.com/ivuorinen" + }, + { + "name": "Raam Dev", + "homepage": "https://github.com/raamdev" + }, + { + "name": "wojas", + "homepage": "https://github.com/wojas" + } + ], + "scripts": { + "lint": "phpcs --warning-severity=0 .", + "lint-all": "phpcs .", + "fix": "phpcbf ." + }, + "require": { + "ext-simplexml": "*", + "ext-curl": "*" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.6" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..2bf8983 --- /dev/null +++ b/composer.lock @@ -0,0 +1,75 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0ed923ad64c14ec9e739afe660e900ad", + "packages": [], + "packages-dev": [ + { + "name": "squizlabs/php_codesniffer", + "version": "3.6.1", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e", + "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2021-10-11T04:00:11+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/config.example.php b/config.example.php index 9396fd6..96401b5 100644 --- a/config.example.php +++ b/config.example.php @@ -1,4 +1,8 @@ -"; +// Send using mail (false just echoes the results) +$send = true; + // File to write ID of last post to $lastfile = "last.txt"; + +// Debug mode active +$debug = false; diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..0e5e07f --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,19 @@ + + + PHP Code Sniffer configuration file. + + + + + + + + + + */vendor/* + + diff --git a/xkcd-mailer.php b/xkcd-mailer.php index bb95884..924a275 100644 --- a/xkcd-mailer.php +++ b/xkcd-mailer.php @@ -1,35 +1,53 @@ * @author wojas * @author Raam Dev + * @link https://github.com/ivuorinen/xkcd-Mailer * @license The MIT License http://www.opensource.org/licenses/mit-license.php * @version 1.0.20140525 - * @link https://github.com/ivuorinen/xkcd-Mailer - **/ + */ // Use config.example.php as base for your configurations. -$lastfile = "last.txt"; -$here = dirname(__FILE__); +$here = __DIR__; if (! is_readable($here . '/config.php')) { die("Please configure me. I don't know where I should sent the comic. (Config file {$here}/config.php missing.)"); } + require_once $here . '/config.php'; -$feed = "http://xkcd.com/atom.xml"; +if (! isset($mail, $from) || empty($mail) || empty($from)) { + die('Configuration values $mail and $from cannot be empty'); +} + +$lastfile = $lastfile ?? 'last.txt'; +$debug = $debug ?? false; +$send = $send ?? false; + +$feed = "https://xkcd.com/atom.xml"; + +/** + * Get xml feed and parse it. + * Checks if http(s):// wrapper is allowed. + * + * @param string $feed Feed to fetch and parse. + * + * @return \SimpleXMLElement + */ +function getFeed(string $feed): SimpleXMLElement +{ + if (ini_get('allow_url_fopen')) { + // https://bugs.php.net/bug.php?id=62577 + return simplexml_load_string(file_get_contents($feed)); + } -// Check if http:// wrapper is allowed -if (ini_get('allow_url_fopen')) { - $data = simplexml_load_file($feed); -} else { // If http:// wrapper is disabled (by allow_url_fopen=0, for example), then fall back on cURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $feed); @@ -40,19 +58,17 @@ if (ini_get('allow_url_fopen')) { $result = curl_exec($ch); curl_close($ch); - $data = simplexml_load_string($result); + return simplexml_load_string($result); } +$data = getFeed($feed); $item = $data->entry[0]; -$last = 0; -if (file_exists($lastfile)) { - $f = fopen($lastfile, 'r'); - $last = (int) fread($f, 1024); - fclose($f); -} +$last = file_exists($lastfile) + ? file_get_contents($lastfile) + : 0; -$parts = explode('/', $item->id); +$parts = explode('/', $item->id, 4); $current = (int) $parts[3]; if ($current > $last) { @@ -60,25 +76,45 @@ if ($current > $last) { preg_match("#title=\"(.+)\"#iU", $item->summary, $t); // To send HTML mail, the Content-type header must be set - //$headers = 'MIME-Version: 1.0' . "\r\n"; - $headers = 'Content-type: text/html; charset=UTF-8' . "\r\n"; - $headers .= 'From: '. $from . "\r\n"; + $headers = "Content-type: text/html; charset=UTF-8\r\n" . + sprintf("From: %s\r\n", $from); - $subject = "xkcd {$date}: {$item->title}"; - $punchline = $t[1]; + $subject = sprintf('xkcd %s: %s', $date, $item->title); + $punchline = $t[1]; - $msg = "

id}\">{$item->title}

\n" - . "Posted {$date}
\n" - . $item->summary."
\n" - . "

{$punchline}

\n"; + $title = sprintf('

%s

', $item->id, $item->title); + $body = sprintf( + 'Posted %s
%s

%s

', + $date, + $item->summary, + $punchline + ); - mail($mail, $subject, $msg, $headers); + $msg = sprintf( + "%s\n%s", + $title, + $body + ); - $f = fopen($lastfile, 'w'); - fwrite($f, $current); - fclose($f); + if ($send) { + mail($mail, $subject, $msg, $headers); + } else { + echo $msg . "\n\n"; + } - echo "New last is $current (was $last)\n"; -} else { - echo "No new XKCD: last=$last current=$current\n"; + $file_write_result = file_put_contents($lastfile, $current); + + if (! $file_write_result) { + echo "Error writing to file: $lastfile\n"; + exit(1); + } + + if ($debug) { + echo "New last is $current (was $last)\n"; + } + exit(0); +} + +if ($debug) { + echo sprintf("No new XKCD: last=%d current=%d\n", $last, $current); }