diff --git a/.env.dist b/.env.dist deleted file mode 100644 index 5fbe054..0000000 --- a/.env.dist +++ /dev/null @@ -1 +0,0 @@ -NOTI_PB= diff --git a/.gitignore b/.gitignore index eeec2f3..08e2af2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,11 @@ vendor/ *~ composer.lock -.env +/config/config.yaml +/.php_cs.cache +!/cache/.gitkeep +/cache/ +!/log/.gitkeep +/log/ +!/bin/pushbullet +/bin/ \ No newline at end of file diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..3912d7a --- /dev/null +++ b/.php_cs @@ -0,0 +1,65 @@ + + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +EOF; + + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules( + [ + '@Symfony' => false, + '@PSR2' => true, + 'header_comment' => ['header' => $header], + 'ordered_class_elements' => true, + 'ordered_imports' => true, + 'no_mixed_echo_print' => ['use' => 'print'], + 'strict_param' => true, + 'strict_comparison' => true, + 'single_import_per_statement' => false, + 'phpdoc_order' => true, + 'array_syntax' => ['syntax' => 'short'], + 'phpdoc_add_missing_param_annotation' => true, + 'psr4' => true, + 'phpdoc_var_without_name' => false, + '@PHP70Migration' => true, + '@PHP70Migration:risky' => true, + '@PHP71Migration' => true, + '@PHP71Migration:risky' => true, + 'no_extra_consecutive_blank_lines' => [ + 'break', + 'continue', + 'extra', + 'return', + 'throw', + 'parenthesis_brace_block', + 'square_brace_block', + 'curly_brace_block' + ], + ] + )->setFinder( + PhpCsFixer\Finder::create() + ->ignoreDotFiles(true) + ->ignoreVCS(true) + ->name('*.php') + ->in([ + 'src', + 'bin', + 'tests', + ]) + ); \ No newline at end of file diff --git a/app.php b/app.php deleted file mode 100644 index 5494850..0000000 --- a/app.php +++ /dev/null @@ -1,20 +0,0 @@ -parse(); -$loader->putEnv(); - -$token = getenv('PB_TOKEN'); - -try { - $pushbullet = new Pushbullet\Pushbullet($token); -} catch (\Exception $e) { - print $e->getMessage() . PHP_EOL; -} - -$pushResult = $pushbullet - ->device('Google Pixel XL') - ->pushNote('Hello!', 'How are you?') - ; -print_r($pushResult); \ No newline at end of file diff --git a/bin/pushbullet b/bin/pushbullet index e69de29..6b2e1a9 100755 --- a/bin/pushbullet +++ b/bin/pushbullet @@ -0,0 +1,34 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], + getenv('PHP_ENV') ?: 'dev'); +$debug = getenv('PHP_DEBUG') !== '0' && !$input->hasParameterOption([ + '--no-debug', + '', + ]) && $env !== 'prod'; + +if ($debug) { + Debug::enable(); +} + +$application = new Application(); +$application->add(new \Symfony\Component\Console\Command\HelpCommand()); +$application->add(new \Symfony\Component\Console\Command\ListCommand()); +$application->run($input, $output); diff --git a/cache/.gitkeep b/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/composer.json b/composer.json index 8a36acf..121b319 100644 --- a/composer.json +++ b/composer.json @@ -9,10 +9,47 @@ }, "require": { "php": ">=7.1", + "hassankhan/config": "^0.9", + "incenteev/composer-parameter-handler": "2.1.*", "ivkos/pushbullet": "^3.1", - "symfony/console": "^3.2", - "symfony/event-dispatcher": "^3.2", + "monolog/monolog": "^1.23", "symfony/config": "^3.2", - "josegonzalez/dotenv": "^3.1" + "symfony/console": "^3.2", + "symfony/dependency-injection": "^3.3", + "symfony/event-dispatcher": "^3.2", + "symfony/yaml": "^3.3", + "tedivm/stash": "^0.14.2" + }, + "autoload-dev": { + "psr-4": { + "Sikofitt\\Tests\\Pushbullet\\": "tests/" + } + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.5", + "phpunit/phpunit": "~5", + "symfony/var-dumper": "^3.3" + }, + "extra": { + "incenteev-parameters": { + "file": "config/config.yaml", + "parameter-key": "pushbullet" } + }, + "config": { + "sort-packages": true, + "platform": { + "php": "7.1" + }, + "bin-dir": "bin", + "optimize-autoloader": true + }, + "scripts": { + "default": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters" + ], + "post-install-cmd": ["@default"], + "post-update-cmd": ["@default"] + } + } diff --git a/config/config.yaml.dist b/config/config.yaml.dist new file mode 100644 index 0000000..ddfeac6 --- /dev/null +++ b/config/config.yaml.dist @@ -0,0 +1,5 @@ +pushbullet: + token: pushbullet_token # https://www.pushbullet.com/#settings/account + push_devices: # devices to push to, bin/pushbullet devices + - chrome + - phone \ No newline at end of file diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Sikofitt/Pushbullet/Console/Application.php b/src/Sikofitt/Pushbullet/Console/Application.php new file mode 100644 index 0000000..22fa75f --- /dev/null +++ b/src/Sikofitt/Pushbullet/Console/Application.php @@ -0,0 +1,141 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\Console; + +use Monolog\Handler\StreamHandler; +use Monolog\Handler\SyslogHandler; +use Monolog\Logger; +use Psr\Container\ContainerInterface; +use Sikofitt\Pushbullet\Console\Command\PushbulletDevicesCommand; +use Sikofitt\Pushbullet\Console\Command\PushbulletNotifierCommand; +use Sikofitt\Pushbullet\DependencyInjection\Container; +use Sikofitt\Pushbullet\DependencyInjection\PushBulletExtension; +use Symfony\Component\Console\Application as SymfonyApplication; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; + +class Application extends SymfonyApplication +{ + protected const APP_VERSION = '0.0.1'; + protected const APP_NAME = 'Pushbullet Notifier'; + + /** + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + private $container; + + /** + * @param array $parameters Parameters to set at runtime. + * + * @throws \LogicException + */ + public function __construct(array $parameters = []) + { + $this->buildContainer($parameters); + + $this->add(new PushbulletNotifierCommand($this->container)); + $this->add(new PushbulletDevicesCommand($this->container)); + + parent::__construct(self::APP_NAME, self::APP_VERSION); + } + /** + * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @return null|string + */ + public function getRootDir(): ?string + { + try { + return $this->container->getParameter('app.root'); + } catch (InvalidArgumentException $i) { + return null; + } + } + + /** + * @param string $rootDir + */ + public function setRootDir(string $rootDir): void + { + $this->container->setParameter('app.root', $rootDir); + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. + * @return int 0 if everything went fine, or an error code + * + */ + public function run( + InputInterface $input = null, + OutputInterface $output = null + ): int { + return parent::run( + $input, + $output + ); // TODO: Change the autogenerated stub + } + + /** + * @return \Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface + */ + public function getParameterBag(): ParameterBagInterface + { + return $this->container->getParameterBag(); + } + + /** + * @param \Psr\Container\ContainerInterface $container + * + * @return $this + */ + public function setContainer(ContainerInterface $container): Application + { + $this->container = $container; + + return $this; + } + + private function buildContainer(array $parameters): void + { + $extension = new PushBulletExtension(); + $parameterBag = new ParameterBag($parameters); + $this->container = new Container($parameterBag); + $this->container->registerExtension($extension); + $this->container->loadFromExtension($extension->getAlias()); + $this->container->setParameter('app.root', dirname(__DIR__, 4)); + + $logFile = sprintf('%s/log/pushbullet.log', $this->container->getParameter('app.root')); + + $handlers[] = new StreamHandler($logFile); + $handlers[] = new SyslogHandler('pushbullet'); + $logger = new Logger('pushbullet', $handlers); + $this->container->set('logger', $logger); + + $this->container->compile(); + } +} diff --git a/src/Sikofitt/Pushbullet/Console/Command/PushbulletDevicesCommand.php b/src/Sikofitt/Pushbullet/Console/Command/PushbulletDevicesCommand.php new file mode 100644 index 0000000..ed118f3 --- /dev/null +++ b/src/Sikofitt/Pushbullet/Console/Command/PushbulletDevicesCommand.php @@ -0,0 +1,351 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\Console\Command; + +use Pushbullet\Exceptions\NotFoundException; +use Pushbullet\Pushbullet; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Command to list available targets from a Pushbullet account. + */ +class PushbulletDevicesCommand extends Command +{ + /** @var array */ + private $table; + + /** @var SymfonyStyle */ + private $io; + + /** @var \Pushbullet\Pushbullet */ + private $pushbullet; + + /** @var string */ + private $type; + + /** @var ContainerInterface */ + private $container; + + /** + * PushbulletDevicesCommand constructor. + * + * @param ContainerInterface $container + * + * @throws RuntimeException + * @throws LogicException + * @throws InvalidArgumentException + * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function __construct(ContainerInterface $container) + { + $this->table = []; + $this->container = $container; + $token = $container->getParameter('pushbullet.token'); + + try { + $this->pushbullet = new Pushbullet($token); + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + + parent::__construct('targets'); + } + + /** + * Configures the current command. + * + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + */ + public function configure(): void + { + $this->setName('targets') + ->setDescription('Gets a list of available pushbullet targets.') + ->setHelp($this->getHelpText()) + ->addArgument( + 'target', + InputArgument::OPTIONAL, + "Which types you want to display.\n[all, devices, contacts, channels]", + 'all' + ); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + */ + public function interact(InputInterface $input, OutputInterface $output): void + { + $this->io = new SymfonyStyle($input, $output); + + $this->type = strtolower($input->getArgument('target')); + + $availableTypes = ['all', 'devices', 'channels', 'contacts']; + + if (false === array_key_exists($this->type, array_flip($availableTypes))) { + throw new InvalidArgumentException('display must be one of [' . implode(', ', $availableTypes) . ']'); + } + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + * @throws \Symfony\Component\Console\Exception\RuntimeException + * @throws LogicException When this abstract method is not implemented + * @throws \Pushbullet\Exceptions\NotFoundException + * @throws \Pushbullet\Exceptions\InvalidTokenException + * @throws \Pushbullet\Exceptions\ConnectionException + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @see setCode() + */ + public function execute(InputInterface $input, OutputInterface $output): ?int + { + $this->io->note('You can push by iden or Nick name'); + $heading = ['Nick name', 'Make', 'Model', 'iden']; + + switch ($this->type) { + case 'devices': + $this->io->text(' Devices'); + $this->addDevicesSection(); + break; + case 'channels': + $heading = ['Name', 'Description', 'iden']; + $this->io->text(' Channels'); + $this->addChannelsSection(); + break; + case 'contacts': + $heading = ['Name', 'Email', 'iden']; + $this->io->text(' Contacts'); + $this->addContactsSection(); + break; + default: + // devices + $this->addDevicesHeading(); + $this->addDevicesSection(); + // channels + $this->addChannelsHeading(); + $this->addChannelsSection(); + // contacts + $this->addContactsHeading(); + $this->addContactsSection(); + break; + } + + if (true !== empty($this->table)) { + $logger = $this->container->get('logger'); + $logger->debug('Creating table'); + $this->io->table($heading, $this->table); + return 0; + } + + $this->io->error('There were no devices found.'); + + return 255; + } + + /** + * @return string + * Help text for this command. + */ + private function getHelpText(): string + { + return 'This command displays your available devices, contacts, and channels.' . PHP_EOL . + 'By default this will display all of your targets.' . PHP_EOL . + 'You can list specific ones by using the argument target.' . PHP_EOL . + '[{path}/bin/pushbullet targets contacts], for example will only display your contact targets.' . PHP_EOL; + } + + /** + * Sets the devices listing header. + */ + private function addDevicesHeading(): void + { + $this->io->text(' Devices'); + } + + /** + * Sets the channels listing header. + */ + private function addChannelsHeading(): void + { + $tableSeparator = new TableSeparator(); + $tableCell = new TableCell(); + $this->table[] = $tableSeparator; + $this->table[] = [ + new TableCell('Channels'), + $tableCell, + $tableCell, + $tableCell + ]; + $this->table[] = new TableSeparator(); + $this->table[] = [ + new TableCell('Name'), + new TableCell('Description'), + new TableCell(), + new TableCell('Iden') + ]; + $this->table[] = new TableSeparator(); + } + + /** + * Sets the contacts listing header. + */ + private function addContactsHeading(): void + { + $this->table[] = new TableSeparator(); + $this->table[] = [ + new TableCell('Contacts'), + new TableCell(), + new TableCell(), + new TableCell() + ]; + $this->table[] = new TableSeparator(); + $this->table[] = [ + new TableCell('Name'), + new TableCell('Email'), + new TableCell(), + new TableCell('Iden') + ]; + $this->table[] = new TableSeparator(); + } + + /** + * Adds the devices section of the table. + * + * @throws \Pushbullet\Exceptions\ConnectionException + * @throws \Pushbullet\Exceptions\InvalidTokenException + */ + private function addDevicesSection(): void + { + try { + $pushbulletDevices = $this->pushbullet->getDevices(); + } catch (\Exception $e) { + $this->io->error($e->getMessage()); + return; + } + + foreach ($pushbulletDevices as $device) { + $this->table[] = [ + $device->nickname, + $device->manufacturer, + $device->model, + $device->iden + ]; + } + } + + /** + * Adds the contacts section of the table. + * + * @throws \Pushbullet\Exceptions\ConnectionException + * @throws \Pushbullet\Exceptions\InvalidTokenException + */ + private function addContactsSection(): void + { + try { + $contacts = $this->pushbullet->getContacts(); + } catch (\Exception $e) { + $this->io->error($e->getMessage()); + return; + } + + foreach ($contacts as $contact) { + if ('all' === $this->type) { + $this->table[] = [ + $contact->name, + $contact->email, + new TableCell(), + $contact->iden + ]; + } else { + $this->table[] = [ + $contact->name, + $contact->email, + $contact->iden, + ]; + } + } + } + + /** + * Adds the channels section of the table. + * + * @throws \Pushbullet\Exceptions\ConnectionException + * @throws \Pushbullet\Exceptions\InvalidTokenException + * @throws \Pushbullet\Exceptions\NotFoundException + */ + private function addChannelsSection(): void + { + try { + $channels = $this->pushbullet->getMyChannels(); + } catch (NotFoundException $n) { + $this->io->error('Could not find any channels.'); + return; + } catch (\Exception $e) { + return; + } + + foreach ($channels as $channel) { + if ('all' === $this->type) { + $this->table[] = [ + $channel->name, + $channel->description, + new TableCell(), + $channel->iden + ]; + } else { + $this->table[] = [ + $channel->name, + $channel->description, + $channel->iden + ]; + } + } + } +} diff --git a/src/Sikofitt/Pushbullet/Console/Command/PushbulletNotifierCommand.php b/src/Sikofitt/Pushbullet/Console/Command/PushbulletNotifierCommand.php new file mode 100644 index 0000000..e0eb637 --- /dev/null +++ b/src/Sikofitt/Pushbullet/Console/Command/PushbulletNotifierCommand.php @@ -0,0 +1,195 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\Console\Command; + +use Pushbullet\Push; +use Pushbullet\Pushbullet; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Command to send notifications through Pushbullet. + */ +class PushbulletNotifierCommand extends Command +{ + /** @var \Pushbullet\Pushbullet */ + private $pushbullet; + + /** @var string */ + private $typeFunction; + + /** + * Constructor. + * + * @param $container ContainerInterface + * + * @throws LogicException When the command name is empty + * @throws \Pushbullet\Exceptions\PushbulletException + * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function __construct(ContainerInterface $container) + { + $token = $container->getParameter('pushbullet.token'); + $this->pushbullet = new Pushbullet($token); + parent::__construct(); + } + + /** + * Configures the current command. + * + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + */ + public function configure(): void + { + $this->setName('notify') + ->setDescription('Notifies using Pushbullet.') + ->addArgument( + 'name', + InputArgument::IS_ARRAY, + 'The name or ident of the device to push to.' + ) + ->addOption( + 'device', + 'd', + InputOption::VALUE_NONE, + 'Push to a device' + ) + ->addOption( + 'channel', + 'c', + InputOption::VALUE_NONE, + 'Push to a channel' + ) + ->addOption( + 'contact', + 't', + InputOption::VALUE_NONE, + 'Push to a contact' + ) + ->addOption( + 'title', + null, + InputOption::VALUE_OPTIONAL, + 'The Title of your note.', + 'Note from ' . getenv('USER') + ) + ->addOption( + 'message', + 'm', + InputOption::VALUE_REQUIRED, + 'Your message. You should quote this value.' + ) + ; + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + */ + public function interact(InputInterface $input, OutputInterface $output): void + { + if (null === $input->getOption('message')) { + throw new InvalidArgumentException('Message MUST be set.'); + } + + if (null === $input->getArgument('name') || true === empty($input->getArgument('name'))) { + throw new InvalidArgumentException('Name must be set'); + } + + $device = $input->getOption('device'); + $channel = $input->getOption('channel'); + $contact = $input->getOption('contact'); + + if ( + ($device && $channel && $contact) || + ($device && $channel) || + ($device && $contact) || + ($channel && $contact) + ) { + throw new InvalidArgumentException('Can only set one target type.'); + } + + if ($device) { + $this->typeFunction = 'device'; + } elseif ($channel) { + $this->typeFunction = 'channel'; + } elseif ($contact) { + $this->typeFunction = 'contact'; + } else { + throw new InvalidArgumentException('Need to set at least one type'); + } + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @throws LogicException When this abstract method is not implemented + * @throws \Pushbullet\Exceptions\NotPushableException + * @throws \Pushbullet\Exceptions\ConnectionException + * @throws \Symfony\Component\Console\Exception\InvalidArgumentException + * @return null|int null or 0 if everything went fine, or an error code + * + * @see setCode() + */ + public function execute(InputInterface $input, OutputInterface $output): ?int + { + $io = new SymfonyStyle($input, $output); + $iden = $input->getArgument('name'); + + $iden = implode(' ', $iden); + /** @var \Pushbullet\Device|\Pushbullet\Contact|\Pushbullet\Channel $type */ + $type = $this->pushbullet->{$this->typeFunction}($iden); + + $title = $input->getOption('title'); + $message = $input->getOption('message'); + + $result = $type->pushNote($title, $message); + + if ($result instanceof Push) { + $io->success('Your note has been pushed!'); + return 0; + } + return 255; + } +} diff --git a/src/Sikofitt/Pushbullet/DependencyInjection/Configuration.php b/src/Sikofitt/Pushbullet/DependencyInjection/Configuration.php new file mode 100644 index 0000000..708c157 --- /dev/null +++ b/src/Sikofitt/Pushbullet/DependencyInjection/Configuration.php @@ -0,0 +1,58 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @throws \RuntimeException + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The + * tree builder + */ + public function getConfigTreeBuilder(): ?TreeBuilder + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('pushbullet'); + $rootNode + ->children() + ->scalarNode('token') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->arrayNode('push_devices') + ->cannotBeEmpty() + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->castToArray() + ->end() + ->scalarPrototype() + ->end() + ->end() + ->end(); + + return $treeBuilder; + } +} diff --git a/src/Sikofitt/Pushbullet/DependencyInjection/Container.php b/src/Sikofitt/Pushbullet/DependencyInjection/Container.php new file mode 100644 index 0000000..1f996d9 --- /dev/null +++ b/src/Sikofitt/Pushbullet/DependencyInjection/Container.php @@ -0,0 +1,44 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\DependencyInjection; + +use Monolog\Handler\StreamHandler; +use Monolog\Logger; +use Psr\Container\ContainerInterface; +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; + +/** + * ContainerBuilder is a DI container that provides an API to easily describe services. + * + * @author Fabien Potencier + */ +class Container extends ContainerBuilder implements ContainerInterface +{ + /** + * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance + */ + public function __construct(ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + } +} diff --git a/src/Sikofitt/Pushbullet/DependencyInjection/PushBulletExtension.php b/src/Sikofitt/Pushbullet/DependencyInjection/PushBulletExtension.php new file mode 100644 index 0000000..4f046dc --- /dev/null +++ b/src/Sikofitt/Pushbullet/DependencyInjection/PushBulletExtension.php @@ -0,0 +1,89 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Sikofitt\Pushbullet\DependencyInjection; + +use \Symfony\Component\Yaml\Yaml; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\Yaml\Exception\ParseException; + +class PushBulletExtension extends Extension +{ + /** + * Loads a specific configuration. + * + * @param array $configs An array of configuration values + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @throws \InvalidArgumentException When provided tag is not defined in + * this extension + */ + public function load(array $configs, ContainerBuilder $container): void + { + $locator = new FileLocator([$container->getParameter('app.root').'/config']); + + try { + $config = $locator->locate('config.yaml'); + } catch (FileLocatorFileNotFoundException $f) { + print $f->getMessage(); + exit; + } + + try { + $configs = Yaml::parse(file_get_contents($config)); + } catch (ParseException $p) { + print sprintf('%sUnable to parse %s at line %d (stopped at "%s") in %s%s', PHP_EOL, $config, $p->getParsedLine(), $p->getSnippet(), PHP_EOL, PHP_EOL); + exit; + } catch (\Exception $e) { + print PHP_EOL . $e->getMessage() . PHP_EOL . PHP_EOL; + exit; + } + + $configuration = new Configuration(); + $processor = new Processor(); + $config = $processor->processConfiguration($configuration, $configs); + $object = new \stdClass(); + $object->class = new \ArrayObject(); + $object->arguments = [ + ['array'] + ]; + $container->set('pushbullet.app', $object); + + $container->setParameter('pushbullet.token', $config['token']); + $container->setParameter('pushbullet.push_devices', $config['push_devices']); + } + + /** + * Returns the recommended alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * @return string The alias + */ + public function getAlias() + { + return 'pushbullet'; + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29