Compare commits

...

20 Commits

Author SHA1 Message Date
Alexey Skobkin 351bffb298
Moved LICENSE. 2023-04-01 21:07:42 +03:00
Alexey Skobkin 312c1617e1
Moved JS/CSS/etc resources from /web to /public. 2023-04-01 21:06:57 +03:00
Alexey Skobkin c908a22d15
Removed useless index.rst. 2023-04-01 21:04:53 +03:00
Alexey Skobkin 53028d2577
Removed useless SkobkinPointToolsBundle. 2023-04-01 21:04:12 +03:00
Alexey Skobkin 1393e2d53f
Ported UserController. Fixed PhpDoc type hinting in SubscriptionEventRepository and UserRepository. 2023-04-01 21:00:16 +03:00
Alexey Skobkin 77fa05f457
Ported PublicFeedController. 2023-04-01 20:31:40 +03:00
Alexey Skobkin 40db2edc38
Ported PostController. 2023-04-01 20:30:04 +03:00
Alexey Skobkin e56cae9568
Ported MainController. 2023-04-01 20:28:24 +03:00
Alexey Skobkin fc9026d13f
Ported EventsController. composer require knplabs/knp-paginator-bundle. 2023-04-01 20:28:24 +03:00
Alexey Skobkin 0c48b57710
Ported ApiController. 2023-04-01 19:57:27 +03:00
Alexey Skobkin a83b783de4
Ported WebHookController. 2023-04-01 19:56:02 +03:00
Alexey Skobkin 430632c8e4
Ported Api\{AbstractApiController, CrawlerController}. 2023-04-01 19:45:21 +03:00
Alexey Skobkin f4e92a8f31
Avoiding an error warning in UserFactory. 2023-04-01 19:40:23 +03:00
Alexey Skobkin 8614bd0f26
Renaming point http client to @app.point.http_client. 2023-04-01 19:38:17 +03:00
Alexey Skobkin a409fbae8d
Ported UserSearchType. 2023-04-01 19:36:13 +03:00
Alexey Skobkin 0822aea5ec
Ported CommandProcessingException. 2023-04-01 19:36:13 +03:00
Alexey Skobkin c7256f5a50
Ported Exception\Factory\Blog\*. 2023-04-01 19:36:13 +03:00
Alexey Skobkin c151acd30b
Ported InvalidUserDataException. 2023-04-01 19:36:13 +03:00
Alexey Skobkin b4bdda26e3
Backslashes for usleep() call. 2023-04-01 19:10:56 +03:00
Alexey Skobkin 0a05264406
Changed value type for $pointApiDelay and set default value of 5 sec. 2023-04-01 19:10:26 +03:00
41 changed files with 316 additions and 208 deletions

View File

@ -12,6 +12,7 @@
"doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.14", "doctrine/orm": "^2.14",
"jms/serializer-bundle": "^5.2", "jms/serializer-bundle": "^5.2",
"knplabs/knp-paginator-bundle": "^6.2",
"phpdocumentor/reflection-docblock": "^5.3", "phpdocumentor/reflection-docblock": "^5.3",
"phpstan/phpdoc-parser": "^1.16", "phpstan/phpdoc-parser": "^1.16",
"sensio/framework-extra-bundle": "^6.1", "sensio/framework-extra-bundle": "^6.1",

162
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3e4ae473183073d1dd231517f5734c3e", "content-hash": "adf20c9a601a7f685ece9da4a4ac9e47",
"packages": [ "packages": [
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
@ -2013,6 +2013,166 @@
], ],
"time": "2023-02-05T11:03:45+00:00" "time": "2023-02-05T11:03:45+00:00"
}, },
{
"name": "knplabs/knp-components",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/KnpLabs/knp-components.git",
"reference": "6b6efa81ee894e325744bf785d50dc962937b1f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/KnpLabs/knp-components/zipball/6b6efa81ee894e325744bf785d50dc962937b1f2",
"reference": "6b6efa81ee894e325744bf785d50dc962937b1f2",
"shasum": ""
},
"require": {
"php": "^8.0",
"symfony/event-dispatcher-contracts": "^3.0"
},
"conflict": {
"doctrine/dbal": "<2.10"
},
"require-dev": {
"doctrine/mongodb-odm": "^2.4",
"doctrine/orm": "^2.12",
"doctrine/phpcr-odm": "^1.6",
"ext-pdo_sqlite": "*",
"jackalope/jackalope-doctrine-dbal": "^1.8",
"phpunit/phpunit": "^9.5",
"propel/propel1": "^1.7",
"ruflin/elastica": "^7.0",
"solarium/solarium": "^6.0",
"symfony/http-foundation": "^5.4 || ^6.0",
"symfony/http-kernel": "^5.4 || ^6.0",
"symfony/property-access": "^5.4 || ^6.0"
},
"suggest": {
"doctrine/common": "to allow usage pagination with Doctrine ArrayCollection",
"doctrine/mongodb-odm": "to allow usage pagination with Doctrine ODM MongoDB",
"doctrine/orm": "to allow usage pagination with Doctrine ORM",
"doctrine/phpcr-odm": "to allow usage pagination with Doctrine ODM PHPCR",
"propel/propel1": "to allow usage pagination with Propel ORM",
"ruflin/elastica": "to allow usage pagination with ElasticSearch Client",
"solarium/solarium": "to allow usage pagination with Solarium Client",
"symfony/http-foundation": "to retrieve arguments from Request",
"symfony/property-access": "to allow sorting arrays"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"Knp\\Component\\": "src/Knp/Component"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "KnpLabs Team",
"homepage": "https://knplabs.com"
},
{
"name": "Symfony Community",
"homepage": "https://github.com/KnpLabs/knp-components/contributors"
}
],
"description": "Knplabs component library",
"homepage": "http://github.com/KnpLabs/knp-components",
"keywords": [
"components",
"knp",
"knplabs",
"pager",
"paginator"
],
"support": {
"issues": "https://github.com/KnpLabs/knp-components/issues",
"source": "https://github.com/KnpLabs/knp-components/tree/v4.1.0"
},
"time": "2022-12-19T09:36:54+00:00"
},
{
"name": "knplabs/knp-paginator-bundle",
"version": "v6.2.0",
"source": {
"type": "git",
"url": "https://github.com/KnpLabs/KnpPaginatorBundle.git",
"reference": "8da698f0856b1d9c9c02dcacbfc382c5c9440c80"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/KnpLabs/KnpPaginatorBundle/zipball/8da698f0856b1d9c9c02dcacbfc382c5c9440c80",
"reference": "8da698f0856b1d9c9c02dcacbfc382c5c9440c80",
"shasum": ""
},
"require": {
"knplabs/knp-components": "^4.1",
"php": "^8.0",
"symfony/config": "^6.0",
"symfony/dependency-injection": "^6.0",
"symfony/event-dispatcher": "^6.0",
"symfony/http-foundation": "^6.0",
"symfony/http-kernel": "^6.0",
"symfony/routing": "^6.0",
"symfony/translation": "^6.0",
"twig/twig": "^3.0"
},
"require-dev": {
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"symfony/expression-language": "^6.0",
"symfony/templating": "^6.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
}
},
"autoload": {
"psr-4": {
"Knp\\Bundle\\PaginatorBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "KnpLabs Team",
"homepage": "https://knplabs.com"
},
{
"name": "Symfony Community",
"homepage": "https://github.com/KnpLabs/KnpPaginatorBundle/contributors"
}
],
"description": "Paginator bundle for Symfony to automate pagination and simplify sorting and other features",
"homepage": "https://github.com/KnpLabs/KnpPaginatorBundle",
"keywords": [
"bundle",
"knp",
"knplabs",
"pager",
"pagination",
"paginator",
"symfony"
],
"support": {
"issues": "https://github.com/KnpLabs/KnpPaginatorBundle/issues",
"source": "https://github.com/KnpLabs/KnpPaginatorBundle/tree/v6.2.0"
},
"time": "2023-03-25T06:51:40+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.3.1", "version": "3.3.1",

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
return [ return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
@ -13,4 +14,5 @@ return [
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
]; ];

View File

@ -0,0 +1,4 @@
# https://github.com/KnpLabs/KnpPaginatorBundle#yaml
knp_paginator:
template:
pagination: 'KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig'

View File

@ -12,14 +12,20 @@ services:
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind: bind:
# TODO: fix retrieval # TODO: fix retrieval
# Point Tools
$appUserId: 435
$appUserLogin: 'point-tools'
# Telegram Bot API # Telegram Bot API
$telegramToken: '' $telegramToken: ''
$debugEnabled: '%kernel.debug%'
# Point API # Point API
$pointDomain: 'point.im' $pointDomain: 'point.im'
$pointScheme: 'https' $pointScheme: 'https'
$pointApiDelay: '' $pointApiDelay: 5000
$pointAppUserId: '' $pointAppUserId: ''
$pointApiClient: '@app.point.http_client' $pointApiClient: '@app.point.http_client'
# Crawler API
$crawlerToken: ''
# makes classes in src/ available to be used as services # makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name # this creates a service per class whose id is the fully-qualified class name
@ -31,8 +37,8 @@ services:
- '../src/Kernel.php' - '../src/Kernel.php'
# HTTP client for Point API # HTTP client for Point API
Symfony\Component\HttpClient\HttpClient: app.point.http_client:
alias: 'app.point.http_client' class: Symfony\Component\HttpClient\HttpClient
factory: [null, 'create'] factory: [null, 'create']
arguments: arguments:
base_uri: '%point_base_url%' base_uri: '%point_base_url%'

View File

@ -83,10 +83,6 @@ knp_markdown:
parser: parser:
service: app.point.markdown_parser service: app.point.markdown_parser
knp_paginator:
template:
pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig
csa_guzzle: csa_guzzle:
profiler: '%kernel.debug%' profiler: '%kernel.debug%'

View File

@ -1,8 +0,0 @@
<?php
namespace src\PointToolsBundle\Exception\Factory\Blog;
class FactoryException extends \Exception
{
}

View File

@ -1,10 +0,0 @@
<?php
namespace src\PointToolsBundle\Exception\Factory\Blog;
use src\PointToolsBundle\Exception\Factory\Blog\FactoryException;
class InvalidDataException extends FactoryException
{
}

View File

@ -1,26 +0,0 @@
<?php
namespace src\PointToolsBundle\Exception\Factory\Blog;
use src\PointToolsBundle\DTO\Api\Post;
use src\PointToolsBundle\Exception\Factory\Blog\InvalidDataException;
class InvalidPostDataException extends InvalidDataException
{
/**
* @var Post
*/
private $post;
public function __construct($message = '', Post $post, $code = 0, \Exception $previous = null)
{
$this->post = $post;
parent::__construct($message, $code, $previous);
}
public function getPost(): Post
{
return $this->post;
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace src\PointToolsBundle\Exception\Factory;
use src\PointToolsBundle\DTO\Api\User as UserDTO;
class InvalidUserDataException extends \Exception
{
/**
* @var UserDTO
*/
private $user;
public function __construct($message = "", UserDTO $user, $code = 0, \Exception $previous = null)
{
$this->user = $user;
parent::__construct($message, $code, $previous);
}
public function getUser(): UserDTO
{
return $this->user;
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace src\PointToolsBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class SkobkinPointToolsBundle extends Bundle
{
}

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -81,7 +81,7 @@ class UpdateSubscriptionsCommand extends Command
$progress->start(count($usersForUpdate)); $progress->start(count($usersForUpdate));
foreach ($usersForUpdate as $user) { foreach ($usersForUpdate as $user) {
usleep($this->pointApiDelay); \usleep($this->pointApiDelay);
$progress->advance(); $progress->advance();
$this->logger->info('Processing @'.$user->getLogin()); $this->logger->info('Processing @'.$user->getLogin());

View File

@ -66,7 +66,7 @@ class UpdateUsersPrivacyCommand extends Command
$progress->start(count($usersForUpdate)); $progress->start(count($usersForUpdate));
foreach ($usersForUpdate as $user) { foreach ($usersForUpdate as $user) {
usleep($this->pointApiDelay); \usleep($this->pointApiDelay);
$progress->advance(); $progress->advance();
$this->logger->info('Processing @'.$user->getLogin()); $this->logger->info('Processing @'.$user->getLogin());

View File

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller\Api; namespace App\Controller\Api;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -15,9 +16,6 @@ class AbstractApiController extends AbstractController
], $code); ], $code);
} }
/**
*
*/
protected function createErrorResponse(string $message, int $code = 400): Response protected function createErrorResponse(string $message, int $code = 400): Response
{ {
return $this->json([ return $this->json([
@ -28,4 +26,4 @@ class AbstractApiController extends AbstractController
] ]
], $code); ], $code);
} }
} }

View File

@ -1,22 +1,19 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller\Api; namespace App\Controller\Api;
use App\DTO\Api\PostsPage;
use App\Factory\Blog\PostFactory;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use JMS\Serializer\SerializerInterface; use JMS\Serializer\SerializerInterface;
use src\PointToolsBundle\DTO\Api\PostsPage;
use src\PointToolsBundle\Service\Factory\Blogs\PostFactory;
use Symfony\Component\HttpFoundation\{Request, Response}; use Symfony\Component\HttpFoundation\{Request, Response};
use src\PointToolsBundle\Controller\Api\AbstractApiController;
class CrawlerController extends AbstractApiController class CrawlerController extends AbstractApiController
{ {
/** @var string */ public function __construct(
private $crawlerToken; private readonly string $crawlerToken,
) {
public function __construct(string $crawlerToken)
{
$this->crawlerToken = $crawlerToken;
} }
public function receiveAllPageAction(Request $request, SerializerInterface $serializer, PostFactory $postFactory, EntityManagerInterface $em): Response public function receiveAllPageAction(Request $request, SerializerInterface $serializer, PostFactory $postFactory, EntityManagerInterface $em): Response
@ -24,7 +21,10 @@ class CrawlerController extends AbstractApiController
$remoteToken = $request->request->get('token'); $remoteToken = $request->request->get('token');
if (!$this->crawlerToken || ($this->crawlerToken !== $remoteToken)) { if (!$this->crawlerToken || ($this->crawlerToken !== $remoteToken)) {
return $this->createErrorResponse('Token error. Please check it in crawler and API parameters.', Response::HTTP_FORBIDDEN); return $this->createErrorResponse(
'Token error. Please check it in crawler and API parameters.',
Response::HTTP_FORBIDDEN,
);
} }
$json = $request->request->get('json'); $json = $request->request->get('json');

View File

@ -1,13 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use src\PointToolsBundle\Entity\User; use App\Entity\{SubscriptionEvent, User};
use src\PointToolsBundle\Entity\{SubscriptionEvent}; use App\Repository\SubscriptionEventRepository;
use src\PointToolsBundle\Repository\SubscriptionEventRepository; use Symfony\Component\HttpFoundation\{JsonResponse, Response};
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
class ApiController class ApiController
{ {

View File

@ -1,9 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use src\PointToolsBundle\Repository\SubscriptionEventRepository; use App\Repository\SubscriptionEventRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{Request, Response}; use Symfony\Component\HttpFoundation\{Request, Response};

View File

@ -1,30 +1,22 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use Doctrine\ORM\EntityManager; use App\Form\UserSearchType;
use src\PointToolsBundle\Form\UserSearchType; use App\Repository\{SubscriptionEventRepository, SubscriptionRepository, UserRepository};
use src\PointToolsBundle\Repository\SubscriptionRepository;
use src\PointToolsBundle\Repository\UserRepository;
use src\PointToolsBundle\Repository\{SubscriptionEventRepository};
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response}; use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
class MainController extends AbstractController class MainController extends AbstractController
{ {
const AJAX_AUTOCOMPLETE_SIZE = 10; private const AJAX_AUTOCOMPLETE_SIZE = 10;
/** @var int */ public function __construct(
private $appUserId; private readonly int $appUserId,
private readonly string $appUserLogin,
/** @var string */ ) {
private $appUserLogin;
public function __construct(int $appUserId, string $appUserLogin)
{
$this->appUserId = $appUserId;
$this->appUserLogin = $appUserLogin;
} }
public function indexAction( public function indexAction(
@ -33,9 +25,6 @@ class MainController extends AbstractController
SubscriptionRepository $subscriptionRepository, SubscriptionRepository $subscriptionRepository,
SubscriptionEventRepository $subscriptionEventRepository SubscriptionEventRepository $subscriptionEventRepository
): Response { ): Response {
/** @var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$form = $this->createForm( $form = $this->createForm(
UserSearchType::class, UserSearchType::class,
null, null,
@ -66,17 +55,9 @@ class MainController extends AbstractController
]); ]);
} }
/** /** Returns user search autocomplete data in JSON */
* Returns user search autocomplete data in JSON public function searchUserAjaxAction(string $login, UserRepository $userRepository): JsonResponse
*
* @param string $login
*
* @return JsonResponse
*/
public function searchUserAjaxAction(string $login, UserRepository $userRepository): Response
{ {
$em = $this->getDoctrine()->getManager();
$result = []; $result = [];
foreach ($userRepository->findUsersLikeLogin($login, self::AJAX_AUTOCOMPLETE_SIZE) as $user) { foreach ($userRepository->findUsersLikeLogin($login, self::AJAX_AUTOCOMPLETE_SIZE) as $user) {

View File

@ -1,11 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use src\PointToolsBundle\Entity\Blogs\Post;
use src\PointToolsBundle\Repository\Blogs\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use App\Entity\Blog\Post;
use App\Repository\Blog\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
class PostController extends AbstractController class PostController extends AbstractController
@ -29,5 +30,4 @@ class PostController extends AbstractController
'post' => $postRepository->getPostWithComments($post->getId()), 'post' => $postRepository->getPostWithComments($post->getId()),
]); ]);
} }
} }

View File

@ -1,17 +1,18 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use src\PointToolsBundle\Repository\Blogs\PostRepository; use App\Repository\Blog\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\{Request, Response};
class PublicFeedController extends AbstractController class PublicFeedController extends AbstractController
{ {
private const POSTS_PER_PAGE = 20; private const POSTS_PER_PAGE = 20;
public function indexAction(Request $request, PostRepository $postRepository, PaginatorInterface $paginator) public function indexAction(Request $request, PostRepository $postRepository, PaginatorInterface $paginator): Response
{ {
$postsPagination = $paginator->paginate( $postsPagination = $paginator->paginate(
$postRepository->createPublicFeedPostsQuery(), $postRepository->createPublicFeedPostsQuery(),

View File

@ -1,28 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller\Telegram; namespace App\Controller\Telegram;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use src\PointToolsBundle\Service\Telegram\IncomingUpdateDispatcher; use src\PointToolsBundle\Service\Telegram\IncomingUpdateDispatcher;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response}; use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
use unreal4u\TelegramAPI\Telegram\Types\Update; use unreal4u\Telegram\Types\Update;
/**
* {@inheritdoc}
*/
class WebHookController extends AbstractController class WebHookController extends AbstractController
{ {
/** @var string */ public function __construct(
private $telegramToken; private readonly string $telegramToken,
private readonly bool $debugEnabled,
/** @var bool */ ) {
private $debug;
public function __construct(string $telegramToken, bool $debug)
{
$this->telegramToken = $telegramToken;
$this->debug = $debug;
} }
public function receiveUpdateAction(Request $request, string $token, IncomingUpdateDispatcher $updateDispatcher, LoggerInterface $logger): Response public function receiveUpdateAction(Request $request, string $token, IncomingUpdateDispatcher $updateDispatcher, LoggerInterface $logger): Response
@ -31,21 +23,20 @@ class WebHookController extends AbstractController
throw $this->createNotFoundException(); throw $this->createNotFoundException();
} }
$content = json_decode($request->getContent(), true); $content = \json_decode($request->getContent(), flags: JSON_THROW_ON_ERROR);
$update = new Update( $update = new Update(
$content, $content,
$logger
); );
try { try {
$updateDispatcher->process($update); $updateDispatcher->process($update);
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->debug) { if ($this->debugEnabled) {
throw $e; throw $e;
} }
$logger->addError('Telegram bot error', [ $logger->error('Telegram bot error', [
'exception' => get_class($e), 'exception' => get_class($e),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),

View File

@ -1,32 +1,24 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Controller; namespace App\Controller;
use App\DTO\{TopUserDTO, DailyEventsDTO};
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use src\PointToolsBundle\DTO\{TopUserDTO};
use src\PointToolsBundle\DTO\DailyEvents;
use src\PointToolsBundle\Entity\User;
use src\PointToolsBundle\Repository\UserRenameEventRepository;
use src\PointToolsBundle\Repository\UserRepository;
use src\PointToolsBundle\Repository\{SubscriptionEventRepository};
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Ob\HighchartsBundle\Highcharts\Highchart; use Ob\HighchartsBundle\Highcharts\Highchart;
use App\Entity\User;
use App\Repository\{SubscriptionEventRepository, UserRenameEventRepository, UserRepository};
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{Request, Response}; use Symfony\Component\HttpFoundation\{Request, Response};
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
class UserController extends AbstractController class UserController extends AbstractController
{ {
/** @var TranslatorInterface */ public function __construct(
private $translator; private readonly TranslatorInterface $translator,
) {
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
} }
/**
* @param string $login
*/
public function showAction( public function showAction(
Request $request, Request $request,
string $login, string $login,
@ -68,16 +60,16 @@ class UserController extends AbstractController
} }
/** /**
* @todo move to the service * @param DailyEventsDTO[] $eventsByDay
*@todo move to the service
* *
* @param DailyEvents[] $eventsByDay
*/ */
private function createEventsDynamicChart(array $eventsByDay = []): Highchart private function createEventsDynamicChart(array $eventsByDay = []): Highchart
{ {
$data = []; $data = [];
foreach ($eventsByDay as $dailyEvents) { foreach ($eventsByDay as $dailyEvents) {
$data[$dailyEvents->getDate()->format('d.m')] = $dailyEvents->getEventsCount(); $data[$dailyEvents->date->format('d.m')] = $dailyEvents->eventsCount;
} }
return $this->createChart('eventschart', 'line', $data, 'Events by day', 'amount'); return $this->createChart('eventschart', 'line', $data, 'Events by day', 'amount');
@ -93,7 +85,7 @@ class UserController extends AbstractController
$data = []; $data = [];
foreach ($topUsers as $topUser) { foreach ($topUsers as $topUser) {
$data[$topUser->getLogin()] = $topUser->getSubscribersCount(); $data[$topUser->login] = $topUser->subscribersCount;
} }
return $this->createChart('topchart', 'bar', $data, 'Top users', 'amount'); return $this->createChart('topchart', 'bar', $data, 'Top users', 'amount');

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Exception\Factory\Blog;
class FactoryException extends \Exception
{
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Exception\Factory\Blog;
class InvalidDataException extends FactoryException
{
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Exception\Factory\Blog;
use App\DTO\Api\Post;
class InvalidPostDataException extends InvalidDataException
{
public function __construct(
public readonly Post $post,
$code = 0,
\Exception $previous = null
) {
parent::__construct('Invalid post data', $code, $previous);
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Exception\Factory;
use App\DTO\Api\User as UserDTO;
class InvalidUserDataException extends \Exception
{
public function __construct(
public readonly UserDTO $user,
$code = 0,
\Exception $previous = null
) {
parent::__construct('Invalid user data', $code, $previous);
}
}

View File

@ -1,9 +1,9 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Exception\Telegram; namespace App\Exception\Telegram;
class CommandProcessingException extends \Exception class CommandProcessingException extends \Exception
{ {
} }

View File

@ -25,13 +25,13 @@ class UserFactory extends AbstractFactory
// @todo LOG // @todo LOG
if (!$userData->isValid()) { if (!$userData->isValid()) {
throw new InvalidUserDataException('Invalid user data', $userData); throw new InvalidUserDataException($userData);
} }
/** @var User $user */ /** @var User $user */
if (null === ($user = $this->userRepository->find($userData->getId()))) { if (null === ($user = $this->userRepository->find($userData->getId()))) {
$user = new User( $user = new User(
$userData->getId(), (int) $userData->getId(),
\DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime() \DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime()
); );
$this->userRepository->add($user); $this->userRepository->add($user);

View File

@ -1,17 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace src\PointToolsBundle\Form; namespace App\Form;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
class UserSearchType extends AbstractType class UserSearchType extends AbstractType
{ {
/** public function buildForm(FormBuilderInterface $builder, array $options): void
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('login') ->add('login')

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Repository; namespace App\Repository;
use App\DTO\DailyEventsDTO;
use App\Entity\SubscriptionEvent; use App\Entity\SubscriptionEvent;
use App\Entity\User; use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
@ -101,14 +102,14 @@ class SubscriptionEventRepository extends ServiceEntityRepository
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
/** @return SubscriptionEvent[] */ /** @return DailyEventsDTO[] */
public function getLastEventsByDay(int $days = 30): array public function getLastEventsByDay(int $days = 30): array
{ {
$qb = $this->createQueryBuilder('se'); $qb = $this->createQueryBuilder('se');
$rows = $qb $rows = $qb
->select([ ->select([
'NEW Skobkin\Bundle\PointToolsBundle\DTO\DailyEvents(DAY(se.date), COUNT(se))', 'NEW App\DTO\DailyEventsDTO(DAY(se.date), COUNT(se))',
'DAY(se.date) as day', 'DAY(se.date) as day',
]) ])
->groupBy('day') ->groupBy('day')

View File

@ -1,9 +1,10 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace App\Repository; namespace App\Repository;
use App\DTO\TopUserDTO;
use App\Entity\Subscription;
use App\Entity\User; use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
@ -114,11 +115,11 @@ class UserRepository extends ServiceEntityRepository
*/ */
public function getTopUsers(int $limit = 30): array public function getTopUsers(int $limit = 30): array
{ {
$qb = $this->getEntityManager()->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s'); $qb = $this->getEntityManager()->getRepository(Subscription::class)->createQueryBuilder('s');
$rows = $qb $rows = $qb
->select([ ->select([
'NEW Skobkin\Bundle\PointToolsBundle\DTO\TopUserDTO(a.login, COUNT(s.subscriber))', 'NEW App\DTO\TopUserDTO(a.login, COUNT(s.subscriber))',
'COUNT(s.subscriber) as subscribers_count' 'COUNT(s.subscriber) as subscribers_count'
]) ])
->innerJoin('s.author', 'a') ->innerJoin('s.author', 'a')

View File

@ -47,6 +47,9 @@
"config/packages/jms_serializer.yaml" "config/packages/jms_serializer.yaml"
] ]
}, },
"knplabs/knp-paginator-bundle": {
"version": "v6.2.0"
},
"phpunit/phpunit": { "phpunit/phpunit": {
"version": "9.6", "version": "9.6",
"recipe": { "recipe": {