Test Setup Failed
Push — master ( 5633a5...21224c )
by Alexey
03:00
created

PrivateMessageProcessor::setPointUserId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 1
cp 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Skobkin\Bundle\PointToolsBundle\Service\Telegram;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Skobkin\Bundle\PointToolsBundle\Entity\Telegram\Account;
7
use Skobkin\Bundle\PointToolsBundle\Entity\User;
8
use Skobkin\Bundle\PointToolsBundle\Exception\Telegram\CommandProcessingException;
9
use Skobkin\Bundle\PointToolsBundle\Repository\SubscriptionEventRepository;
10
use Skobkin\Bundle\PointToolsBundle\Repository\SubscriptionRepository;
11
use Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository;
12
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
13
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory;
14
use Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi;
15
use unreal4u\TelegramAPI\Telegram\Types\Message;
16
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup;
17
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardRemove;
18
19
/**
20
 * Processes all private messages
21
 */
22
class PrivateMessageProcessor
23
{
24
    /**
25
     * @var MessageSender
26
     */
27
    private $messenger;
28
29
    /**
30
     * @var UserApi
31
     */
32
    private $userApi;
33
34
    /**
35
     * @var AccountFactory
36
     */
37
    private $accountFactory;
38
39
    /**
40
     * @var EntityManagerInterface
41
     */
42
    private $em;
43
44
    /**
45
     * @var UserRepository
46
     */
47
    private $userRepo;
48
49
    /**
50
     * @var AccountRepository
51
     */
52
    private $accountRepo;
53
54
    /**
55
     * @var SubscriptionRepository
56
     */
57
    private $subscriptionRepo;
58
59
    /**
60
     * @var SubscriptionEventRepository
61
     */
62
    private $subscriptionEventRepo;
63
64
    /**
65
     * @var int
66
     */
67
    private $pointUserId;
68
69
70
    public function __construct(
71
        EntityManagerInterface $em,
72
        UserRepository $userRepository,
73
        AccountRepository $accountRepository,
74
        SubscriptionRepository $subscriptionRepository,
75
        SubscriptionEventRepository $subscriptionRecordRepository,
76
        MessageSender $messageSender,
77
        UserApi $userApi,
78
        AccountFactory $accountFactory
79
    ) {
80
        $this->em = $em;
81
        $this->userRepo = $userRepository;
82
        $this->accountRepo = $accountRepository;
83
        $this->subscriptionRepo = $subscriptionRepository;
84
        $this->subscriptionEventRepo = $subscriptionRecordRepository;
85
        $this->messenger = $messageSender;
86
        $this->userApi = $userApi;
87
        $this->accountFactory = $accountFactory;
88
    }
89
90
    public function setPointUserId(int $pointUserId)
91
    {
92
        $this->pointUserId = $pointUserId;
93
    }
94
95
    public function process(Message $message): void
96
    {
97
        if (!IncomingUpdateDispatcher::CHAT_TYPE_PRIVATE === $message->chat->type) {
98
            throw new \LogicException('This service can process only private chat messages');
99
        }
100
101
        try {
102
            // Registering Telegram user
103
            /** @var Account $account */
104
            $account = $this->accountFactory->findOrCreateFromMessage($message);
105
            $this->em->flush();
106
        } catch (\Exception $e) {
107
            // Low-level message in case of incorrect $account
108
            $this->messenger->sendMessageToChat($message->chat->id, 'There was an error during your Telegram account registration. Try again or report the bug.');
109
        }
110
111
        try {
112
            $words = explode(' ', $message->text, 10);
113
114
            if (0 === count($words)) {
115
                return;
116
            }
117
118
            switch ($words[0]) {
119
                case '/link':
120
                case 'link':
121
                    $this->processLink($account, $words);
122
                    break;
123
124
                case '/me':
125
                case 'me':
126
                    $this->processMe($account);
127
                    break;
128
129
                case '/last':
130
                case 'last':
131
                    $this->processLast($account, $words);
132
                    break;
133
134
                case '/sub':
135
                case 'sub':
136
                    $this->processSub($account, $words);
137
                    break;
138
139
                case '/stats':
140
                case 'stats':
141
                    $this->processStats($account);
142
                    break;
143
144
                // Settings menu
145
                case '/set':
146
                    $this->processSet($account, $words);
147
                    break;
148
149
                // Exit from any menu and remove keyboard
150
                case '/exit':
151
                case 'exit':
152
                    $this->processExit($account);
153
                    break;
154
155
                case '/help':
156
                default:
157
                    $this->processHelp($account);
158
                    break;
159
            }
160
        } catch (CommandProcessingException $e) {
161
            $this->sendError($account, 'Processing error', $e->getMessage());
162
163
            if ($e->getPrevious()) {
164
                throw $e->getPrevious();
165
            }
166
        } catch (\Exception $e) {
167
            $this->sendError($account, 'Unknown error');
168
169
            throw $e;
170
        }
171
    }
172
173
    private function linkAccount(Account $account, string $login, string $password): bool
174
    {
175
        /** @var User $user */
176
        if (null === $user = $this->userRepo->findUserByLogin($login)) {
177
            throw new CommandProcessingException('User not found in Point Tools database. Please try again later.');
178
        }
179
180
        if ($this->userApi->isLoginAndPasswordValid($login, $password)) {
181
            $account->setUser($user);
182
183
            return true;
184
        }
185
186
        return false;
187
    }
188
189
    private function processLink(Account $account, array $words): void
190
    {
191
        if (array_key_exists(2, $words)) {
192
            if ($this->linkAccount($account, $words[1], $words[2])) {
193
                // Saving linking status
194
                $this->em->flush();
195
                $this->sendAccountLinked($account);
196
            } else {
197
                $this->sendError($account, 'Account linking error', 'Check login and password or try again later.');
198
            }
199
        } else {
200
            $this->sendError($account, 'Login/Password error', 'You need to specify login and password separated by space after /link (example: `/link mylogin MypASSw0rd`)');
201
        }
202
    }
203
204
    private function processMe(Account $account): void
205
    {
206
        if ($user = $account->getUser()) {
207
            $this->sendUserEvents($account, $user);
208
        } else {
209
            $this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.');
210
        }
211
    }
212
213
    private function processLast(Account $account, array $words): void
214
    {
215
        if (array_key_exists(1, $words)) {
216 View Code Duplication
            if (null !== $user = $this->userRepo->findUserByLogin($words[1])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
                $this->sendUserEvents($account, $user);
218
            } else {
219
                $this->sendError($account, 'User not found');
220
            }
221
        } else {
222
            $this->sendGlobalEvents($account);
223
        }
224
    }
225
226
    private function processSub(Account $account, array $words): void
227
    {
228
        if (array_key_exists(1, $words)) {
229 View Code Duplication
            if (null !== $user = $this->userRepo->findUserByLogin($words[1])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
                $this->sendUserSubscribers($account, $user);
231
            } else {
232
                $this->sendError($account, 'User not found');
233
            }
234
        } else {
235
            if ($user = $account->getUser()) {
236
                $this->sendUserSubscribers($account, $user);
237
            } else {
238
                $this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.');
239
            }
240
        }
241
    }
242
243
    private function processStats(Account $account): void
244
    {
245
        $this->sendStats($account);
246
    }
247
248
    private function processSet(Account $account, array $words): void
249
    {
250
        $keyboard = new ReplyKeyboardMarkup();
251
252
        if (array_key_exists(1, $words)) {
253
            if (array_key_exists(2, $words)) {
254
                if ('renames' === $words[2]) {
255
                    $account->toggleRenameNotification();
256
                    $this->em->flush();
257
258
                    $this->messenger->sendMessage($account, 'Renaming notifications are turned '.($account->isRenameNotification() ? 'on' : 'off'));
259
                } elseif ('subscribers' === $words[2]) {
260
                    $account->toggleSubscriberNotification();
261
                    $this->em->flush();
262
263
                    $this->messenger->sendMessage($account, 'Subscribers notifications are turned '.($account->isSubscriberNotification() ? 'on' : 'off'));
264
265
                    if ($account->isSubscriberNotification() && null === $account->getUser()) {
266
                        $this->messenger->sendMessage($account, 'You need to /link you account to receive these notifications.');
267
                    }
268
                } else {
269
                    $this->sendError($account, 'Notification type does not exist.');
270
                }
271
            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272
                $keyboard->keyboard = [
273
                    ['/set notifications renames'],
274
                    ['/set notifications subscribers'],
275
                    ['exit'],
276
                ];
277
278
                $this->messenger->sendMessage($account, 'Choose which notification type to toggle', MessageSender::PARSE_PLAIN, $keyboard);
279
            }
280
281
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
            $keyboard->keyboard = [
283
                ['/set notifications'],
284
                ['exit'],
285
            ];
286
287
            $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard);
288
        }
289
    }
290
291
    /**
292
     * Processes exit from keyboard menus and removes the keyboard
293
     */
294
    private function processExit(Account $account): void
295
    {
296
        $keyboardRemove = new ReplyKeyboardRemove();
297
298
        $this->messenger->sendMessage($account, 'Done', MessageSender::PARSE_PLAIN, $keyboardRemove);
299
    }
300
301
    private function processHelp(Account $account): void
302
    {
303
        $this->sendHelp($account);
304
    }
305
306
    private function sendAccountLinked(Account $account): void
307
    {
308
        $this->messenger->sendMessage($account, 'Account linked. Try using /me now.');
309
    }
310
311
    private function sendUserSubscribers(Account $account, User $user): void
312
    {
313
        $subscribers = [];
314
        foreach ($user->getSubscribers() as $subscription) {
315
            $subscribers[] = '@'.$subscription->getSubscriber()->getLogin();
316
        }
317
318
        $this->messenger->sendTemplatedMessage(
319
            $account,
320
            '@SkobkinPointTools/Telegram/user_subscribers.md.twig',
321
            [
322
                'user' => $user,
323
                'subscribers' => $subscribers,
324
            ]
325
        );
326
    }
327
328
    private function sendUserEvents(Account $account, User $user): void
329
    {
330
        $events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10);
331
332
        $this->messenger->sendTemplatedMessage(
333
            $account,
334
            '@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig',
335
            [
336
                'user' => $user,
337
                'events' => $events,
338
            ]
339
        );
340
    }
341
342
    private function sendGlobalEvents(Account $account): void
343
    {
344
        $events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10);
345
346
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]);
347
    }
348
349
    private function sendStats(Account $account): void
350
    {
351
        $this->messenger->sendTemplatedMessage(
352
            $account,
353
            '@SkobkinPointTools/Telegram/stats.md.twig',
354
            [
355
                'total_users' => $this->userRepo->getUsersCount(),
356
                'active_users' => $this->subscriptionRepo->getUserSubscribersCountById($this->pointUserId),
357
                'telegram_accounts' => $this->accountRepo->getAccountsCount(),
358
                'telegram_linked_accounts' => $this->accountRepo->getLinkedAccountsCount(),
359
                'today_events' => $this->subscriptionEventRepo->getLastDayEventsCount(),
360
            ]
361
        );
362
    }
363
364
    private function sendHelp(Account $account): void
365
    {
366
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig');
367
    }
368
369
    private function sendError(Account $account, string $title, string $text = ''): void
370
    {
371
        $this->messenger->sendTemplatedMessage(
372
            $account,
373
            '@SkobkinPointTools/Telegram/error.md.twig',
374
            [
375
                'title' => $title,
376
                'text' => $text,
377
            ]
378
        );
379
    }
380
381
382
}