diff --git a/README.md b/README.md index 0b731e1..1a61f6a 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,18 @@ You can use following jobs as an example: 0 0 * * * /usr/bin/php /path/to/point-tools/app/console point:update:subscriptions --all-users --env=prod ``` +## Setting Telegram webhook (to enable bot) + +```shell +php app/console telegram:webhook set your-domain.tld +``` + +## Removing Telegram webhook + +```shell +php app/console telegram:webhook delete +``` + # Running tests ## Configure environment variables diff --git a/app/AppKernel.php b/app/AppKernel.php index 4fc2eb2..989e0bd 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -19,6 +19,7 @@ class AppKernel extends Kernel new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new JMS\SerializerBundle\JMSSerializerBundle(), new Misd\GuzzleBundle\MisdGuzzleBundle(), + new Csa\Bundle\GuzzleBundle\CsaGuzzleBundle(), new Ob\HighchartsBundle\ObHighchartsBundle(), new Knp\Bundle\MarkdownBundle\KnpMarkdownBundle(), new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(), diff --git a/app/config/config.yml b/app/config/config.yml index 5629e37..0c0123d 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -89,4 +89,7 @@ knp_markdown: knp_paginator: template: - pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig \ No newline at end of file + pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig + +csa_guzzle: + profiler: '%kernel.debug%' \ No newline at end of file diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index 7744534..07bc49d 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -23,4 +23,8 @@ parameters: secret: ThisTokenIsNotSoSecretChangeIt # Token for Go crawler https://bitbucket.org/skobkin/point-tools-crawler - crawler_token: test_token \ No newline at end of file + crawler_token: test_token + + # Telegram bot + telegram_token: "123456:some-token" + telegram_max_connections: 2 diff --git a/composer.json b/composer.json index 2cfb226..c061f8a 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,9 @@ "doctrine/doctrine-migrations-bundle": "^1.0", "jms/serializer-bundle": "^1.1", "knplabs/knp-markdown-bundle": "^1.4", - "knplabs/knp-paginator-bundle": "^2.5" + "knplabs/knp-paginator-bundle": "^2.5", + "unreal4u/telegram-api": "^2.2", + "csa/guzzle-bundle": "^2.1" }, "require-dev": { "sensio/generator-bundle": "~2.3", diff --git a/composer.lock b/composer.lock index 9d792cf..1a3d6b5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,69 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "2afa2daba03d5922ce606ef250dae660", + "content-hash": "f890dca974c5dbd0ada78afc457f249b", "packages": [ + { + "name": "csa/guzzle-bundle", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/csarrazi/CsaGuzzleBundle.git", + "reference": "9796e5c28e02d5c2f0aa1376e8390b1d29159d28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/csarrazi/CsaGuzzleBundle/zipball/9796e5c28e02d5c2f0aa1376e8390b1d29159d28", + "reference": "9796e5c28e02d5c2f0aa1376e8390b1d29159d28", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.1", + "namshi/cuzzle": "^2.0", + "php": ">=5.5.0", + "symfony/dependency-injection": "^2.6|^3.0", + "symfony/expression-language": "^2.6|^3.0", + "symfony/filesystem": "^2.3|^3.0", + "symfony/framework-bundle": "^2.6|^3.0", + "twig/twig": "^1.12|^2.0" + }, + "conflict": { + "guzzlehttp/command": "*", + "guzzlehttp/guzzle-services": "*" + }, + "require-dev": { + "doctrine/cache": "^1.1", + "phpunit/phpunit": "^4.8", + "symfony/phpunit-bridge": "^2.7|^3.0", + "symfony/web-profiler-bundle": "^2.3|^3.0" + }, + "suggest": { + "doctrine/cache": "Allows caching of responses" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Csa\\Bundle\\GuzzleBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Charles Sarrazin", + "email": "charles@sarraz.in" + } + ], + "description": "A bundle integrating GuzzleHttp >= 4.0", + "time": "2016-08-25T12:09:22+00:00" + }, { "name": "doctrine/annotations", "version": "v1.3.0", @@ -1004,6 +1065,177 @@ "abandoned": "guzzlehttp/guzzle", "time": "2015-03-18T18:23:50+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "6.2.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.3.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2016-10-08T15:01:37+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2016-06-24T23:00:38+00:00" + }, { "name": "incenteev/composer-parameter-handler", "version": "v2.1.2", @@ -1809,6 +2041,56 @@ ], "time": "2016-11-26T00:15:39+00:00" }, + { + "name": "namshi/cuzzle", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/namshi/cuzzle.git", + "reference": "89849bb9c729a3d8aabf94c0b66e77c7df38abda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/namshi/cuzzle/zipball/89849bb9c729a3d8aabf94c0b66e77c7df38abda", + "reference": "89849bb9c729a3d8aabf94c0b66e77c7df38abda", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": ">=5.5.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Namshi\\Cuzzle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nikita Nefedov", + "email": "inefedor@gmail.com" + }, + { + "name": "cirpo", + "email": "alessandro.cinelli@gmail.com" + } + ], + "description": "Get the cURL shell command from a Guzzle request", + "time": "2016-11-23T08:01:36+00:00" + }, { "name": "ob/highcharts-bundle", "version": "1.5", @@ -2072,6 +2354,56 @@ ], "time": "2015-07-25T16:39:46+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/log", "version": "1.0.2", @@ -2880,6 +3212,56 @@ ], "time": "2016-11-23T18:41:40+00:00" }, + { + "name": "unreal4u/telegram-api", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/unreal4u/telegram-api.git", + "reference": "dd6f58387569d045bf2581dff909f7874bf7d27a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/unreal4u/telegram-api/zipball/dd6f58387569d045bf2581dff909f7874bf7d27a", + "reference": "dd6f58387569d045bf2581dff909f7874bf7d27a", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~6.0", + "php": ">=7.0.0", + "psr/log": "~1.0" + }, + "require-dev": { + "monolog/monolog": "~1.17", + "phpunit/php-code-coverage": "~3.3", + "phpunit/phpunit": ">=5.2", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "unreal4u\\TelegramAPI\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Camilo Sperberg", + "email": "me@unreal4u.com", + "homepage": "https://github.com/unreal4u/telegram-api/graphs/contributors" + } + ], + "description": "A complete Telegram bot API implementation written in PHP, with support for inline bots!", + "keywords": [ + "api", + "telegram", + "telegram bot" + ], + "time": "2016-12-04T10:20:14+00:00" + }, { "name": "zendframework/zend-code", "version": "2.6.3", diff --git a/src/Skobkin/Bundle/PointToolsBundle/Command/TelegramSetWebHookCommand.php b/src/Skobkin/Bundle/PointToolsBundle/Command/TelegramSetWebHookCommand.php new file mode 100644 index 0000000..a4232d1 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Command/TelegramSetWebHookCommand.php @@ -0,0 +1,79 @@ +setName('telegram:webhook') + ->setDescription('Set webhook') + ->addArgument('mode', InputArgument::REQUIRED, 'Command mode (set or delete)') + ->addArgument('host', InputArgument::OPTIONAL, 'Host of telegram hook') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $container = $this->getContainer(); + $telegramClient = $container->get('point_tools.telegram.api_client'); + + if (self::MODE_SET === strtolower($input->getArgument('mode'))) { + if (!$input->hasArgument('host')) { + throw new \InvalidArgumentException('Host must be specified when using --set flag'); + } + + $router = $container->get('router'); + $token = $container->getParameter('telegram_token'); + + $url = sprintf( + 'https://%s%s', + $input->getArgument('host'), + $router->generate('telegram_webhook', ['token' => $token]) + ); + + $output->writeln('Setting webhook: '.$url); + + $setWebHook = new SetWebhook(); + $setWebHook->url = $url; + $setWebHook->max_connections = (int) $container->getParameter('telegram_max_connections'); + + $telegramClient->performApiRequest($setWebHook); + + $output->writeln('Done'); + } elseif (self::MODE_DELETE === strtolower($input->getArgument('mode'))) { + $output->writeln('Deleting webhook'); + + $deleteWebHook = new DeleteWebhook(); + + $telegramClient->performApiRequest($deleteWebHook); + + $output->writeln('Done'); + } else { + throw new \InvalidArgumentException(sprintf('Mode must be exaclty one of: %s', implode(', ', [self::MODE_SET, self::MODE_DELETE]))); + } + + return 0; + } +} diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/Telegram/WebHookController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/Telegram/WebHookController.php new file mode 100644 index 0000000..666ac6e --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/Telegram/WebHookController.php @@ -0,0 +1,32 @@ +getParameter('telegram_token')) { + throw $this->createNotFoundException(); + } + + $content = json_decode($request->getContent(), true); + + $update = new Update( + $content, + $this->get('logger') + ); + + $this->get('point_tools.telegram.update_processor')->process($update); + + return new JsonResponse('received'); + } +} diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php index 6485677..ce0cf6b 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php @@ -53,14 +53,14 @@ class User /** * @var ArrayCollection * - * @ORM\OneToMany(targetEntity="Subscription", mappedBy="author") + * @ORM\OneToMany(targetEntity="Subscription", mappedBy="author", fetch="EXTRA_LAZY") */ private $subscribers; /** * @var ArrayCollection * - * @ORM\OneToMany(targetEntity="Subscription", mappedBy="subscriber") + * @ORM\OneToMany(targetEntity="Subscription", mappedBy="subscriber", fetch="EXTRA_LAZY") */ private $subscriptions; diff --git a/src/Skobkin/Bundle/PointToolsBundle/Repository/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Repository/SubscriptionEventRepository.php index 0e69548..8a8bef5 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Repository/SubscriptionEventRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Repository/SubscriptionEventRepository.php @@ -48,7 +48,7 @@ class SubscriptionEventRepository extends EntityRepository } /** - * Get last subscriptions QueryBuilder for pagination + * Get last global subscriptions QueryBuilder for pagination * * @return QueryBuilder */ @@ -63,4 +63,19 @@ class SubscriptionEventRepository extends EntityRepository ->orderBy('se.date', 'desc') ; } + + /** + * Get last global subscription events + * + * @param int $limit + * + * @return SubscriptionEvent[] + */ + public function getLastSubscriptionEvents($limit = 20) + { + $qb = $this->createLastSubscriptionEventsQuery(); + $qb->setMaxResults($limit); + + return $qb->getQuery()->getResult(); + } } \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml index 74cd91d..c50eedf 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml @@ -2,6 +2,14 @@ index: path: / defaults: { _controller: SkobkinPointToolsBundle:Main:index } +telegram_webhook: + path: /telegram/webhook/{token} + defaults: { _controller: SkobkinPointToolsBundle:Telegram/WebHook:receiveUpdate, _format: json } + methods: [POST] + requirements: + token: "\d+\:[\w-]+" + _format: json + user_search_ajax: path: /ajax/users/search/{login} defaults: { _controller: SkobkinPointToolsBundle:Main:searchUserAjax, _format: json } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml index 20e8cce..935bceb 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml @@ -1,4 +1,13 @@ services: + # Guzzle 6 client for Telegram + point_tools.http.telegram_client: + class: GuzzleHttp\Client + arguments: [{ timeout: 3.0 }] + tags: + - { name: csa_guzzle.client } + + # Guzzle 5 client for Point API + # TODO upgrade and remove misd bundle skobkin_point_tools.http_client: class: %guzzle.client.class% arguments: [ "%point_base_url%" ] @@ -68,8 +77,23 @@ services: # Twig extensions point_tools.twig.point_avatar_extension: - class: Skobkin\Bundle\PointToolsBundle\Twig\PointAvatarExtension + class: Skobkin\Bundle\PointToolsBundle\Twig\PointUserExtension public: false arguments: ['@skobkin_point_tools.api_user'] tags: - - { name: twig.extension } \ No newline at end of file + - { name: twig.extension } + + # Telegram API client + point_tools.telegram.api_client: + class: unreal4u\TelegramAPI\TgLog + arguments: [%telegram_token%, @logger, @point_tools.http.telegram_client] + + # Telegram simple message sender + point_tools.telegram.simple_sender: + class: Skobkin\Bundle\PointToolsBundle\Service\Telegram\SimpleSender + arguments: [@point_tools.telegram.api_client] + + # Telegram command processor + point_tools.telegram.update_processor: + class: Skobkin\Bundle\PointToolsBundle\Service\Telegram\IncomingUpdateProcessor + arguments: [%point_id%, @point_tools.telegram.api_client, @doctrine.orm.entity_manager, @twig] \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/help.md.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/help.md.twig new file mode 100644 index 0000000..562b7ad --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/help.md.twig @@ -0,0 +1,7 @@ +*Help*: + +/last - shows last global subscription events +/last %user% - shows last user subscribers events +/help shows this message + +Visit [Point Tools](https://point.skobk.in/) for more info. \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/last_global_subscriptions.md.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/last_global_subscriptions.md.twig new file mode 100644 index 0000000..ab13fe1 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/last_global_subscriptions.md.twig @@ -0,0 +1,11 @@ +Last global subscription events: + +{% set subscription = constant('\\Skobkin\\Bundle\\PointToolsBundle\\Entity\\SubscriptionEvent::ACTION_SUBSCRIBE') %} +{# @var event \Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionEvent #} +{% for event in events %} +{% set sub_login = event.subscriber.login %} +{% set author_login = event.author.login %} +{{ event.date|date('d M H:i') }} [@{{ sub_login }}]({{ sub_login|point_user_url(true) }}){% if subscription == event.action %} -> {% else %} X {% endif %}[@{{ author_login }}]({{ author_login|point_user_url(true) }}) +{% endfor %} + +See more events on [Point Tools](https://point.skobk.in/) site. \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/stats.md.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/stats.md.twig new file mode 100644 index 0000000..2f0589a --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Telegram/stats.md.twig @@ -0,0 +1,7 @@ +*Point Tools statistics:* + +Total users: {{ total_users }} +Active users: {{ active_users }} +24 hours events: {{ today_events }} + +Visit [Point Tools](https://point.skobk.in/) for more info. \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig index 1d44924..75ebe28 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig @@ -5,7 +5,7 @@ {% block content %}

- {{ user.login }} + {{ user.login }}

diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/IncomingUpdateProcessor.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/IncomingUpdateProcessor.php new file mode 100644 index 0000000..2315798 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/IncomingUpdateProcessor.php @@ -0,0 +1,170 @@ +client = $client; + $this->em = $em; + $this->twig = $twig; + $this->pointUserId = $pointUserId; + } + + /** + * Processes update and delegates it to corresponding service + * + * @param Update $update + */ + public function process(Update $update) + { + if ($update->message && $update->message instanceof Message) { + $chatType = $update->message->chat->type; + + if (self::CHAT_TYPE_PRIVATE === $chatType) { + $this->processPrivateMessage($update); + } elseif (self::CHAT_TYPE_GROUP === $chatType) { + + } + } elseif ($update->inline_query && $update->inline_query instanceof Query) { + $this->processInlineQuery($update); + } + + } + + /** + * @todo refactor + * + * @param Update $update + */ + private function processPrivateMessage(Update $update) + { + $chatId = $update->message->chat->id; + $text = $update->message->text; + + $sendMessage = new SendMessage(); + $sendMessage->chat_id = $chatId; + $sendMessage->parse_mode = self::PARSE_MODE_MARKDOWN; + $sendMessage->disable_web_page_preview = true; + + $words = explode(' ', $text, 3); + + if (0 === count($words)) { + return; + } + + switch ($words[0]) { + case 'l': + case '/last': + case 'last': + if (array_key_exists(1, $words)) { + $sendMessage->text = 'Not implemented yet :('; + } else { + $events = $this->em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(10); + $sendMessage->text = $this->twig->render('@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]); + } + + break; + + case 'sub': + case '/sub': + case 'subscribers': + $sendMessage->text = 'Subscribers list here...'; + break; + + case 'stats': + case '/stats': + $stats = [ + 'total_users' => $this->em->getRepository('SkobkinPointToolsBundle:User')->getUsersCount(), + 'active_users' => $this->em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->pointUserId), + 'today_events' => $this->em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(), + ]; + + $sendMessage->text = $this->twig->render('@SkobkinPointTools/Telegram/stats.md.twig', $stats); + + break; + + case '/help': + default: + $sendMessage->text = $this->twig->render('@SkobkinPointTools/Telegram/help.md.twig'); + break; + } + + $this->client->performApiRequest($sendMessage); + } + + private function processInlineQuery(Update $update) + { + $queryId = $update->inline_query->id; + $text = $update->inline_query->query; + + if (mb_strlen($text) < 2) { + return; + } + + $answerInlineQuery = new AnswerInlineQuery(); + $answerInlineQuery->inline_query_id = $queryId; + + foreach ($this->em->getRepository('SkobkinPointToolsBundle:User')->findUsersLikeLogin($text) as $user) { + $article = new Query\Result\Article(); + $article->title = $user->getLogin(); + + $contentText = new Text(); + $contentText->message_text = sprintf( + "@%s:\nName: %s\nSubscribers: %d", + $user->getLogin(), + $user->getName(), + $user->getSubscribers()->count() + ); + + $article->input_message_content = $contentText; + $article->id = md5($user->getId()); + + $answerInlineQuery->addResult($article); + } + + $this->client->performApiRequest($answerInlineQuery); + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/SimpleSender.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/SimpleSender.php new file mode 100644 index 0000000..da62a35 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Telegram/SimpleSender.php @@ -0,0 +1,49 @@ +client = $client; + } + + /** + * Send simple message + * + * @param int $chatId + * @param string $text + * + * @return bool + */ + public function sendMessage(int $chatId, string $text): bool + { + $sendMessage = new SendMessage(); + $sendMessage->chat_id = $chatId; + $sendMessage->text = $text; + + try { + $this->client->performApiRequest($sendMessage); + + return true; + } catch (ClientException $e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Twig/PointAvatarExtension.php b/src/Skobkin/Bundle/PointToolsBundle/Twig/PointAvatarExtension.php deleted file mode 100644 index cee18d4..0000000 --- a/src/Skobkin/Bundle/PointToolsBundle/Twig/PointAvatarExtension.php +++ /dev/null @@ -1,53 +0,0 @@ -userApi = $userApi; - } - - public function getFunctions() - { - return [ - new \Twig_SimpleFunction('point_avatar', [$this, 'pointAvatarFunction']), - new \Twig_SimpleFunction('point_avatar_small', [$this, 'pointAvatarSmallFunction']), - new \Twig_SimpleFunction('point_avatar_medium', [$this, 'pointAvatarMediumFunction']), - new \Twig_SimpleFunction('point_avatar_large', [$this, 'pointAvatarLargeFunction']), - ]; - } - - public function pointAvatarSmallFunction($login) - { - return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_SMALL); - } - - public function pointAvatarMediumFunction($login) - { - return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_MEDIUM); - } - - public function pointAvatarLargeFunction($login) - { - return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_LARGE); - } - - public function pointAvatarFunction($login, $size) - { - return $this->userApi->getAvatarUrlByLogin($login, $size); - } - - public function getName() - { - return 'point_tools_avatars'; - } -} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Twig/PointUserExtension.php b/src/Skobkin/Bundle/PointToolsBundle/Twig/PointUserExtension.php new file mode 100644 index 0000000..5338876 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Twig/PointUserExtension.php @@ -0,0 +1,86 @@ +userApi = $userApi; + } + + public function getFunctions() + { + return [ + new \Twig_SimpleFunction('point_avatar', [$this, 'pointAvatarFunction']), + new \Twig_SimpleFunction('point_avatar_small', [$this, 'pointAvatarSmallFunction']), + new \Twig_SimpleFunction('point_avatar_medium', [$this, 'pointAvatarMediumFunction']), + new \Twig_SimpleFunction('point_avatar_large', [$this, 'pointAvatarLargeFunction']), + new \Twig_SimpleFunction('point_user_url', [$this, 'pointUserUrl']), + new \Twig_SimpleFunction('point_user_blog_url', [$this, 'pointUserBlogUrl']), + ]; + } + + public function getFilters() + { + return [ + new \Twig_SimpleFilter('point_avatar', [$this, 'pointAvatarFunction']), + new \Twig_SimpleFilter('point_avatar_small', [$this, 'pointAvatarSmallFunction']), + new \Twig_SimpleFilter('point_avatar_medium', [$this, 'pointAvatarMediumFunction']), + new \Twig_SimpleFilter('point_avatar_large', [$this, 'pointAvatarLargeFunction']), + new \Twig_SimpleFilter('point_user_url', [$this, 'pointUserUrl']), + new \Twig_SimpleFilter('point_user_blog_url', [$this, 'pointUserBlogUrl']), + ]; + } + + public function pointAvatarSmallFunction(string $login): string + { + return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_SMALL); + } + + public function pointAvatarMediumFunction(string $login): string + { + return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_MEDIUM); + } + + public function pointAvatarLargeFunction(string $login): string + { + return $this->pointAvatarFunction($login, UserApi::AVATAR_SIZE_LARGE); + } + + public function pointAvatarFunction(string $login, $size): string + { + return $this->userApi->getAvatarUrlByLogin($login, $size); + } + + /** + * @param string $login + * @param bool $forceHttps + * + * @return string + */ + public function pointUserUrl(string $login, bool $forceHttps = false): string + { + return sprintf('%s//%s.%s/', $forceHttps ? 'https' : '', $login, self::POINT_HOST); + } + + /** + * @param string $login + * @param bool $forceHttps + * + * @return string + */ + public function pointUserBlogUrl(string $login, bool $forceHttps = false): string + { + return sprintf('%s//%s.%s/blog/', $forceHttps ? 'https' : '', $login, self::POINT_HOST); + } +} \ No newline at end of file