* koin service and cli stuff added

This commit is contained in:
Danyi Dávid
2017-09-05 23:04:28 +02:00
parent 946d00ff93
commit 8184c1e07d
15 changed files with 801 additions and 55 deletions

View File

@@ -2,62 +2,23 @@
namespace App\Action;
use App\Service\KoinService;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Expressive\Router;
use Zend\Expressive\Template;
use Zend\Expressive\Plates\PlatesRenderer;
use Zend\Expressive\Twig\TwigRenderer;
use Zend\Expressive\ZendView\ZendViewRenderer;
use Zend\Diactoros\Response\TextResponse;
class HomePageAction implements ServerMiddlewareInterface
{
private $router;
private $tmp;
private $template;
public function __construct(Router\RouterInterface $router, Template\TemplateRendererInterface $template = null)
public function __construct(KoinService $tmp)
{
$this->router = $router;
$this->template = $template;
$this->tmp = $tmp;
}
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
if (! $this->template) {
return new JsonResponse([
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
'docsUrl' => 'https://docs.zendframework.com/zend-expressive/',
]);
}
$data = [];
if ($this->router instanceof Router\AuraRouter) {
$data['routerName'] = 'Aura.Router';
$data['routerDocs'] = 'http://auraphp.com/packages/2.x/Router.html';
} elseif ($this->router instanceof Router\FastRouteRouter) {
$data['routerName'] = 'FastRoute';
$data['routerDocs'] = 'https://github.com/nikic/FastRoute';
} elseif ($this->router instanceof Router\ZendRouter) {
$data['routerName'] = 'Zend Router';
$data['routerDocs'] = 'https://docs.zendframework.com/zend-router/';
}
if ($this->template instanceof PlatesRenderer) {
$data['templateName'] = 'Plates';
$data['templateDocs'] = 'http://platesphp.com/';
} elseif ($this->template instanceof TwigRenderer) {
$data['templateName'] = 'Twig';
$data['templateDocs'] = 'http://twig.sensiolabs.org/documentation';
} elseif ($this->template instanceof ZendViewRenderer) {
$data['templateName'] = 'Zend View';
$data['templateDocs'] = 'https://docs.zendframework.com/zend-view/';
}
return new HtmlResponse($this->template->render('app::home-page', $data));
return new TextResponse("Nuff");
}
}

View File

@@ -2,19 +2,14 @@
namespace App\Action;
use App\Service\KoinService;
use Interop\Container\ContainerInterface;
use Zend\Expressive\Router\RouterInterface;
use Zend\Expressive\Template\TemplateRendererInterface;
class HomePageFactory
{
public function __invoke(ContainerInterface $container)
{
$router = $container->get(RouterInterface::class);
$template = $container->has(TemplateRendererInterface::class)
? $container->get(TemplateRendererInterface::class)
: null;
return new HomePageAction($router, $template);
$tmp = $container->get(KoinService::class);
return new HomePageAction($tmp);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Command;
use App\Entity\Sms;
use App\Service\KoinService;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class KoinImportCommand extends Command
{
/**
* @var KoinService
*/
private $koinService;
/**
* @var EntityManager
*/
private $em;
public function __construct(KoinService $koinService,
EntityManager $em)
{
$this->koinService = $koinService;
$this->em = $em;
parent::__construct();
}
protected function configure()
{
$this->setName('import:koin')
->setDescription('Sends sms to koin')
->addArgument(
'id',
InputArgument::REQUIRED,
'Db id of the SMS'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$smsId = $input->getArgument('id');
/** @var Sms $sms */
$sms = $this->em->getRepository(Sms::class)->find($smsId);
$this->koinService->onReceiveSms($sms);
$output->writeln("OK");
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Command;
use App\Service\KoinService;
use Interop\Container\ContainerInterface;
class KoinImportCommandFactory
{
public function __invoke(ContainerInterface $container)
{
/** @var KoinService $koinService */
$koinService = $container->get(KoinService::class);
$em = $container->get('doctrine.entity_manager.orm_default');
return new KoinImportCommand($koinService, $em);
}
}

View File

@@ -41,6 +41,7 @@ class ConfigProvider
Action\StoreAction::class => Action\StoreFactory::class,
Service\SmsStoreService::class => Service\SmsStoreServiceFactory::class,
Service\KoinService::class => Service\KoinServiceFactory::class,
],
];
}

View File

@@ -0,0 +1,241 @@
<?php
namespace App\Service;
use App\Entity\Sms;
use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;
class KoinService
{
const BASE_URI = 'https://www.koin.hu';
const DEFAULT_EXPENSE = 'Egyéb kiadás';
/**
* 1 - date
* 2 - time
* 3 - amount contains dot as thousand separator
* 4 - currency
* 5 - pos info
*/
const OTP_SMS_PATTERN = '/([0-9]{6}) ([0-9]{1,2}:[0-9]{1,2}) K[áà]rty[áà]s v[áà]s[áà]rl[áà]s/z[áà]rol[áà]s: -([0-9.]+) ([A-Z]{2,3}); (.*?); K[áà]rtyasz[áà]m: ...[0-9]{4}; Egyenleg: \+[0-9.]+ HUF - OTPdirekt/msi';
/**
* @var array
*/
private $config;
/**
* @var Client
*/
private $httpClient;
/**
* @var string
*/
private $csrfParam;
/**
* @var string
*/
private $csrfToken;
/**
* @var array
*/
private $expenseCategories = [];
/**
* @var array
*/
private $incomeCategories = [];
public function __construct(Client $httpClient, array $config)
{
$this->config = $config;
$this->httpClient = $httpClient;
}
/**
* @param Sms $sms
*/
public function onReceiveSms(Sms $sms)
{
if (false !== preg_match(self::OTP_SMS_PATTERN, $sms->getText(), $otpMatches)) {
$datePart = implode("-", sscanf($otpMatches[1], '%2c%2c%2c'));
$this->saveTransaction(
intval(str_replace(".","", $otpMatches[3])),
$otpMatches[4],
"20${datePart} ${otpMatches['2']}",
$this->getExpenseCategory($otpMatches[5]),
$this->getExpenseTags($otpMatches[5])
);
}
}
/**
* @param string $posInfo
* @return string
*/
private function getExpenseCategory(string $posInfo): string
{
return self::DEFAULT_EXPENSE;
}
/**
* @param string $posInfo
* @return array
*/
private function getExpenseTags(string $posInfo): array
{
return [];
}
/**
* @param int $amount
* @param string $currency
* @param string $date
* @param string $category
* @param array $tags
* @return int
*/
public function saveTransaction(int $amount, string $currency = 'HUF', string $date, string $category, array $tags): int
{
$pageData = $this->login($this->config['koin.user'], $this->config['koin.pass']);
$this->loadFormData($pageData->getBody());
return $this->postData(
$amount,
$currency,
$date,
$this->getCategoryId($category),
$this->getTagsString($tags)
);
}
/**
* @param string $user
* @param string $pass
* @return \Psr\Http\Message\ResponseInterface
*/
private function login(string $user, string $pass): ResponseInterface
{
$httpResponse = $this->httpClient->get(self::BASE_URI);
$body = $httpResponse->getBody();
$this->getCsrfToken($body);
$httpResponse = $this->httpClient
->post(self::BASE_URI . "/site/login", [
'form_params' => [
$this->csrfParam => $this->csrfToken,
'LoginForm[email]' => $user,
'LoginForm[pass]' => $pass,
'LoginForm[rememberMe]' => 0,
],
]);
return $httpResponse;
}
/**
* @param $htmlData
*/
private function loadFormData($htmlData)
{
$documentXpath = $this->getCsrfToken($htmlData);
/** @var \DOMNodeList $expenseOptionElements */
$expenseOptionElements = $documentXpath->query('//select[@id="transactioncreate-category_id"]/optgroup[@label="Kiadás"]/option');
/** @var \DOMNodeList $incomeOptionElements */
$incomeOptionElements = $documentXpath->query('//select[@id="transactioncreate-category_id"]/optgroup[@label="Bevétel"]/option');
/** @var \DOMElement $element */
foreach ($expenseOptionElements as $element) {
$this->expenseCategories[$element->nodeValue] = $element->getAttribute("value");
}
/** @var \DOMElement $element */
foreach ($incomeOptionElements as $element) {
$this->incomeCategories[$element->nodeValue] = $element->getAttribute("value");
}
}
/**
* Parse csrf from html
*
* @param $html
* @return \DOMXPath
*/
private function getCsrfToken($html): \DOMXPath
{
$xmlErrorHandling = libxml_use_internal_errors(TRUE);
$domDocument = new \DOMDocument();
$domDocument->loadHTML($html);
libxml_clear_errors();
libxml_use_internal_errors($xmlErrorHandling);
$documentXpath = new \DOMXPath($domDocument);
/** @var \DOMElement $paramElement */
$paramElement = $documentXpath->query('//meta[@name="csrf-param"]')->item(0);
/** @var \DOMElement $tokenElement */
$tokenElement = $documentXpath->query('//meta[@name="csrf-token"]')->item(0);
$this->csrfParam = $paramElement->getAttribute("content");
$this->csrfToken = $tokenElement->getAttribute("content");
return $documentXpath;
}
/**
* @param int $amount
* @param string $currency
* @param string $date
* @param string $category
* @param string $tags
* @return int
*/
private function postData(int $amount, string $currency = 'HUF', string $date, string $category, string $tags): int
{
$saveResponse = $this->httpClient
->post(self::BASE_URI . "/main/save-transaction", [
'form_params' => [
$this->csrfParam => $this->csrfToken,
'TransactionCreate[category_id]' => $category,
'TransactionCreate[value]' => $amount,
'TransactionCreate[currency]' => $currency,
'TransactionCreate[imgKey]' => "",
'TransactionCreate[tagData]' => $tags,
'TransactionCreate[date]' => $date,
],
]);
return $saveResponse->getStatusCode();
}
/**
* @param array $tags
* @return string
*/
private function getTagsString(array $tags): string
{
return implode(';', $tags);
}
/**
* @param string $category
* @return string
*/
private function getCategoryId(string $category): string
{
if(in_array($category, array_keys($this->expenseCategories))) {
return $this->expenseCategories[$category];
}
return $this->expenseCategories[self::DEFAULT_EXPENSE];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Service;
use GuzzleHttp\Client;
use Interop\Container\ContainerInterface;
class KoinServiceFactory
{
public function __invoke(ContainerInterface $container)
{
$httpClient = new Client([
'cookies' => true,
]);
$config = $container->get('config');
return new KoinService($httpClient, $config);
}
}

View File

@@ -13,6 +13,7 @@ class SmsStoreServiceFactory
public function __invoke(ContainerInterface $container)
{
$em = $container->get('doctrine.entity_manager.orm_default');
return new SmsStoreService($em);
}
}