User renaming Telegram notifications. User renaming event 'app.users.renamed' present. UpdateSubscriptionsCommand refactored (now updates data in single transaction). SubscriptionsManager updated (now not flushing). Point API clients and factories not flushing data themselves. Possible bugs added.

This commit is contained in:
Alexey Skobkin 2017-01-07 00:38:20 +03:00
parent 6c198053d5
commit 4e24b58c49
19 changed files with 220 additions and 118 deletions

View File

@ -60,6 +60,9 @@ class UpdateSubscriptionsCommand extends ContainerAwareCommand
return 1;
}
// Beginning transaction for all changes
$em->beginTransaction();
if ($input->getOption('all-users')) {
$usersForUpdate = $userRepository->findAll();
} else {
@ -144,6 +147,10 @@ class UpdateSubscriptionsCommand extends ContainerAwareCommand
usleep(500000);
}
// Flushing all changes at once to database
$em->flush();
$em->commit();
return 0;
}
}

View File

@ -28,6 +28,8 @@ class CrawlerController extends AbstractApiController
$continue = $factory->createFromPageDTO($page);
$this->getDoctrine()->getManager()->flush();
return $this->createSuccessResponse([
'continue' => $continue,
]);

View File

@ -12,7 +12,7 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
* @ORM\Index(name="subscriber_notification_idx", columns={"subscriber_notification"}, options={"where": "subscriber_notification = TRUE"}),
* @ORM\Index(name="rename_notification_idx", columns={"rename_notification"}, options={"where": "rename_notification = TRUE"}),
* })
* @ORM\Entity
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository")
* @ORM\HasLifecycleCallbacks()
*/
class Account

View File

@ -10,6 +10,8 @@ use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Entity\UserRenameEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class UserRenameSubscriber implements EventSubscriber
{
@ -18,6 +20,22 @@ class UserRenameSubscriber implements EventSubscriber
*/
private $items = [];
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
/**
* UserRenameSubscriber constructor.
*
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
public function getSubscribedEvents()
{
return [
@ -43,6 +61,9 @@ class UserRenameSubscriber implements EventSubscriber
public function postFlush(PostFlushEventArgs $event)
{
if (0 !== count($this->items)) {
// Creating event for dispatch
$usersRenamedEvent = new GenericEvent(null, $this->items);
$em = $event->getEntityManager();
foreach ($this->items as $item) {
@ -52,6 +73,8 @@ class UserRenameSubscriber implements EventSubscriber
$this->items = [];
$em->flush();
$this->eventDispatcher->dispatch('app.users.renamed', $usersRenamedEvent);
}
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\EventListener;
use Skobkin\Bundle\PointToolsBundle\Service\Telegram\Notifier;
use Symfony\Component\EventDispatcher\GenericEvent;
class UsersRenameNotifierListener
{
/**
* @var Notifier
*/
private $notifier;
/**
* UsersRenameNotifierListener constructor.
*
* @param Notifier $notifier
*/
public function __construct(Notifier $notifier)
{
$this->notifier = $notifier;
}
public function onAppUsersRenamed(GenericEvent $event)
{
$this->notifier->sendUsersRenamedNotification((array) $event->getIterator());
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Repository\Telegram;
use Doctrine\ORM\EntityRepository;
class AccountRepository extends EntityRepository
{
}

View File

@ -87,9 +87,16 @@ services:
# Event listener
point_tools.event_listener.user_rename_subscriber:
class: Skobkin\Bundle\PointToolsBundle\EventListener\UserRenameSubscriber
arguments: [@event_dispatcher]
tags:
- { name: doctrine.event_subscriber, connection: default }
point_tools.event_listener.users_renamed_notifier_listener:
class: Skobkin\Bundle\PointToolsBundle\EventListener\UsersRenameNotifierListener
arguments: [@point_tools.telegram.notifier]
tags:
- { name: kernel.event_listener, event: app.users.renamed }
# Twig extensions
point_tools.twig.point_avatar_extension:
@ -109,7 +116,12 @@ services:
# Message sender
point_tools.telegram.message_sender:
class: Skobkin\Bundle\PointToolsBundle\Service\Telegram\MessageSender
arguments: [@point_tools.telegram.api_client]
arguments: [@point_tools.telegram.api_client, @twig]
# User notifier
point_tools.telegram.notifier:
class: Skobkin\Bundle\PointToolsBundle\Service\Telegram\Notifier
arguments: [@doctrine.orm.entity_manager, @point_tools.telegram.message_sender]
# Common incoming message processor
point_tools.telegram.update_dispatcher:
@ -133,5 +145,4 @@ services:
- @skobkin_point_tools.api_user
- @point_tools.factory.telegram_account
- @doctrine.orm.entity_manager
- @twig
- %point_id%

View File

@ -0,0 +1,8 @@
*Following users recently renamed themselves:*
{# @var event \Skobkin\Bundle\PointToolsBundle\Entity\UserRenameEvent #}
{% for event in events %}
{% set login_old = event.oldLogin %}
{% set login_new = event.user.login %}
@{{ login_old }} -> [@{{ login_new }}]({{ login_new|point_user_url(true) }})
{% endfor %}

View File

@ -71,12 +71,6 @@ class CommentFactory
$this->em->persist($comment);
}
try {
$this->em->flush($comment);
} catch (\Exception $e) {
throw new ApiException(sprintf('Error while flushing changes for #%s/%d: %s', $data['post_id'], $data['id'], $e->getMessage()), 0, $e);
}
return $comment;
}

View File

@ -77,8 +77,6 @@ class FileFactory
$this->em->persist($file);
}
$this->em->flush($file);
return $file;
}

View File

@ -72,6 +72,7 @@ class PostFactory
* @param PostsPage $page
*
* @return bool
*
* @throws ApiException
* @throws InvalidResponseException
*/
@ -106,8 +107,6 @@ class PostFactory
}
}
$this->em->flush();
return $hasNew;
}
@ -168,13 +167,6 @@ class PostFactory
throw $e;
}
try {
$this->em->flush($post);
} catch (\Exception $e) {
$this->log->error('Error while flushing post entity');
throw $e;
}
return $post;
}

View File

@ -74,8 +74,6 @@ class TagFactory
$this->em->persist($tag);
}
$this->em->flush($tag);
return $tag;
}

View File

@ -57,12 +57,6 @@ class UserFactory
->setName($data['name'])
;
try {
$this->em->flush($user);
} catch (\Exception $e) {
throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
}
return $user;
}
@ -90,12 +84,6 @@ class UserFactory
->setName($userData->getName())
;
try {
$this->em->flush($user);
} catch (\Exception $e) {
throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
}
return $user;
}

View File

@ -22,9 +22,4 @@ class PostApi extends AbstractApi
$this->postFactory = $postFactory;
}
public function getName()
{
return 'skobkin_point_tools_api_post';
}
}

View File

@ -2,7 +2,6 @@
namespace Skobkin\Bundle\PointToolsBundle\Service;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\QueryBuilder;
use Skobkin\Bundle\PointToolsBundle\Entity\Subscription;
@ -65,8 +64,6 @@ class SubscriptionsManager
$user->addNewSubscriberEvent($logEvent);
}
}
unset($subscribedList);
@ -94,8 +91,6 @@ class SubscriptionsManager
;
unset($unsubscribedList);
$this->em->flush();
}
/**
@ -103,6 +98,7 @@ class SubscriptionsManager
*
* @param User[] $list1
* @param User[] $list2
*
* @return User[] Diff
*/
public function getUsersListsDiff(array $list1 = [], array $list2 = [])

View File

@ -13,46 +13,89 @@ use unreal4u\TelegramAPI\TgLog;
*/
class MessageSender
{
const PARSE_MODE_NOPARSE = '';
const PARSE_MODE_MARKDOWN = 'Markdown';
const PARSE_MODE_HTML5 = 'HTML';
const PARSE_PLAIN = '';
const PARSE_MARKDOWN = 'Markdown';
const PARSE_HTML5 = 'HTML';
/**
* @var TgLog
*/
private $client;
/**
* @var \Twig_Environment
*/
private $twig;
/**
* @param TgLog $client
*/
public function __construct(TgLog $client)
public function __construct(TgLog $client, \Twig_Environment $twig)
{
$this->client = $client;
$this->twig = $twig;
}
public function sendMessageToUser(
/**
* @param Account[] $accounts
* @param string $template
* @param array $templateData
* @param KeyboardMethods|null $keyboardMarkup
* @param bool $disableWebPreview
* @param bool $disableNotifications
* @param string $parseMode
*/
public function sendMassTemplatedMessage(
array $accounts,
string $template,
array $templateData = [],
KeyboardMethods $keyboardMarkup = null,
bool $disableWebPreview = true,
bool $disableNotifications = false,
string $parseMode = self::PARSE_MARKDOWN
) {
$text = $this->twig->render($template, $templateData);
foreach ($accounts as $account) {
$this->sendMessage($account, $text, $parseMode, $keyboardMarkup, $disableWebPreview, $disableNotifications);
}
}
public function sendTemplatedMessage(
Account $account,
string $template,
array $templateData = [],
KeyboardMethods $keyboardMarkup = null,
bool $disableWebPreview = true,
bool $disableNotifications = false,
string $parseMode = self::PARSE_MARKDOWN
): bool {
$text = $this->twig->render($template, $templateData);
return $this->sendMessage($account, $text, $parseMode, $keyboardMarkup, $disableWebPreview, $disableNotifications);
}
public function sendMessage(
Account $account,
string $text,
string $parseMode = self::PARSE_MODE_NOPARSE,
string $parseMode = self::PARSE_PLAIN,
KeyboardMethods $keyboardMarkup = null,
bool $disableWebPreview = false,
bool $disableNotifications = false
): bool
{
): bool {
return $this->sendMessageToChat($account->getChatId(), $text, $parseMode, $keyboardMarkup, $disableWebPreview, $disableNotifications);
}
public function sendMessageToChat(
public function sendMessageToChat(
int $chatId,
string $text,
string $parseMode = self::PARSE_MODE_NOPARSE,
string $parseMode = self::PARSE_PLAIN,
KeyboardMethods $keyboardMarkup = null,
bool $disableWebPreview = false,
bool $disableNotifications = false
): bool
{
): bool {
$sendMessage = new SendMessage();
$sendMessage->chat_id = (string) $chatId;
$sendMessage->chat_id = (string)$chatId;
$sendMessage->text = $text;
$sendMessage->parse_mode = $parseMode;
$sendMessage->disable_web_page_preview = $disableWebPreview;

View File

@ -0,0 +1,53 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Telegram;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Skobkin\Bundle\PointToolsBundle\Entity\UserRenameEvent;
/**
* Notifies Telegram users about some events
*/
class Notifier
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var EntityRepository
*/
private $accountsRepo;
/**
* @var MessageSender
*/
private $messenger;
/**
* Notifier constructor.
*
* @param EntityManagerInterface $em
* @param MessageSender $messenger
*/
public function __construct(EntityManagerInterface $em, MessageSender $messenger)
{
$this->em = $em;
$this->messenger = $messenger;
$this->accountsRepo = $em->getRepository('SkobkinPointToolsBundle:Telegram\Account');
}
/**
* @param UserRenameEvent[] $userRenameEvents
*/
public function sendUsersRenamedNotification(array $userRenameEvents)
{
$accounts = $this->accountsRepo->findBy(['renameNotification' => true]);
$this->messenger->sendMassTemplatedMessage($accounts, '@SkobkinPointTools/Telegram/users_renamed_notification.md.twig', ['events' => $userRenameEvents]);
}
}

View File

@ -11,7 +11,6 @@ use Skobkin\Bundle\PointToolsBundle\Repository\SubscriptionRepository;
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory;
use Skobkin\Bundle\PointToolsBundle\Service\UserApi;
use unreal4u\TelegramAPI\Abstracts\KeyboardMethods;
use unreal4u\TelegramAPI\Telegram\Types\Message;
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup;
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardRemove;
@ -41,11 +40,6 @@ class PrivateMessageProcessor
*/
private $em;
/**
* @var \Twig_Environment
*/
private $twig;
/**
* @var UserRepository
*/
@ -67,20 +61,12 @@ class PrivateMessageProcessor
private $pointUserId;
public function __construct(
MessageSender $messageSender,
UserApi $userApi,
AccountFactory $accountFactory,
EntityManagerInterface $em,
\Twig_Environment $twig,
int $pointUserId
)
public function __construct(MessageSender $messageSender, UserApi $userApi, AccountFactory $accountFactory, EntityManagerInterface $em, int $pointUserId)
{
$this->messenger = $messageSender;
$this->userApi = $userApi;
$this->accountFactory = $accountFactory;
$this->em = $em;
$this->twig = $twig;
$this->pointUserId = $pointUserId;
$this->userRepo = $em->getRepository('SkobkinPointToolsBundle:User');
@ -251,12 +237,12 @@ class PrivateMessageProcessor
$account->toggleRenameNotification();
$this->em->flush();
$this->sendPlainTextMessage($account, 'Renaming notifications are turned '.($account->isRenameNotification() ? 'on' : 'off'));
$this->messenger->sendMessage($account, 'Renaming notifications are turned '.($account->isRenameNotification() ? 'on' : 'off'));
} elseif ('subscribers' === $words[2]) {
$account->toggleSubscriberNotification();
$this->em->flush();
$this->sendPlainTextMessage($account, 'Subscribers notifications are turned '.($account->isSubscriberNotification() ? 'on' : 'off'));
$this->messenger->sendMessage($account, 'Subscribers notifications are turned '.($account->isSubscriberNotification() ? 'on' : 'off'));
} else {
$this->sendError($account, 'Notification type does not exist.');
}
@ -267,7 +253,7 @@ class PrivateMessageProcessor
['exit'],
];
$this->sendPlainTextMessage($account, 'Choose which notification type to toggle', $keyboard);
$this->messenger->sendMessage($account, 'Choose which notification type to toggle', MessageSender::PARSE_PLAIN, $keyboard);
}
} else {
@ -276,7 +262,7 @@ class PrivateMessageProcessor
['exit'],
];
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard);
$this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard);
}
}
@ -287,7 +273,7 @@ class PrivateMessageProcessor
{
$keyboardRemove = new ReplyKeyboardRemove();
$this->sendPlainTextMessage($account, 'Done', $keyboardRemove);
$this->messenger->sendMessage($account, 'Done', MessageSender::PARSE_PLAIN, $keyboardRemove);
}
private function processHelp(Account $account)
@ -297,7 +283,7 @@ class PrivateMessageProcessor
private function sendAccountLinked(Account $account)
{
$this->messenger->sendMessageToUser($account, 'Account linked. Try using /me now.');
$this->messenger->sendMessage($account, 'Account linked. Try using /me now.');
}
private function sendUserSubscribers(Account $account, User $user)
@ -307,7 +293,7 @@ class PrivateMessageProcessor
$subscribers[] = '@'.$subscription->getSubscriber()->getLogin();
}
$this->sendTemplatedMessage(
$this->messenger->sendTemplatedMessage(
$account,
'@SkobkinPointTools/Telegram/user_subscribers.md.twig',
[
@ -321,7 +307,7 @@ class PrivateMessageProcessor
{
$events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10);
$this->sendTemplatedMessage(
$this->messenger->sendTemplatedMessage(
$account,
'@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig',
[
@ -335,12 +321,12 @@ class PrivateMessageProcessor
{
$events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10);
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]);
$this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]);
}
private function sendStats(Account $account)
{
$this->sendTemplatedMessage(
$this->messenger->sendTemplatedMessage(
$account,
'@SkobkinPointTools/Telegram/stats.md.twig',
[
@ -353,12 +339,12 @@ class PrivateMessageProcessor
private function sendHelp(Account $account)
{
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig');
$this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig');
}
private function sendError(Account $account, string $title, string $text = '')
{
$this->sendTemplatedMessage(
$this->messenger->sendTemplatedMessage(
$account,
'@SkobkinPointTools/Telegram/error.md.twig',
[
@ -368,22 +354,5 @@ class PrivateMessageProcessor
);
}
private function sendTemplatedMessage(
Account $account,
string $template,
array $templateData = [],
KeyboardMethods $keyboardMarkup = null,
bool $disableWebPreview = true,
string $format = MessageSender::PARSE_MODE_MARKDOWN
): bool
{
$text = $this->twig->render($template, $templateData);
return $this->messenger->sendMessageToUser($account, $text, $format, $keyboardMarkup, $disableWebPreview, false);
}
private function sendPlainTextMessage(Account $account, string $text, KeyboardMethods $keyboardMarkup = null, bool $disableWebPreview = true): bool
{
return $this->messenger->sendMessageToUser($account, $text, MessageSender::PARSE_MODE_NOPARSE, $keyboardMarkup, $disableWebPreview);
}
}

View File

@ -53,11 +53,6 @@ class UserApi extends AbstractApi
$this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User');
}
public function getName()
{
return 'skobkin_point_tools_api_user';
}
public function isAuthDataValid(string $login, string $password): bool
{
$auth = $this->authenticate($login, $password);
@ -292,12 +287,6 @@ class UserApi extends AbstractApi
->setName($userInfo['name'])
;
try {
$this->em->flush($user);
} catch (\Exception $e) {
throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
}
return $user;
}
@ -336,12 +325,6 @@ class UserApi extends AbstractApi
->setName($userInfo['name'])
;
try {
$this->em->flush($user);
} catch (\Exception $e) {
throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
}
$resultUsers[] = $user;
} else {
throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');