Passed
Push — master ( 9a0d37...3db25e )
by Alexey
15:10
created

PrivateMessageProcessor::processSet()   D

Complexity

Conditions 9
Paths 6

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 42
ccs 0
cts 20
cp 0
rs 4.909
cc 9
eloc 27
nc 6
nop 2
crap 90
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\UserRepository;
12
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory;
13
use Skobkin\Bundle\PointToolsBundle\Service\UserApi;
14
use unreal4u\TelegramAPI\Telegram\Types\Message;
15
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup;
16
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardRemove;
17
18
/**
19
 * Processes all private messages
20
 */
21
class PrivateMessageProcessor
22
{
23
    /**
24
     * @var MessageSender
25
     */
26
    private $messenger;
27
28
    /**
29
     * @var UserApi
30
     */
31
    private $userApi;
32
33
    /**
34
     * @var AccountFactory
35
     */
36
    private $accountFactory;
37
38
    /**
39
     * @var EntityManagerInterface
40
     */
41
    private $em;
42
43
    /**
44
     * @var UserRepository
45
     */
46
    private $userRepo;
47
48
    /**
49
     * @var SubscriptionRepository
50
     */
51
    private $subscriptionRepo;
52
53
    /**
54
     * @var SubscriptionEventRepository
55
     */
56
    private $subscriptionEventRepo;
57
58
    /**
59
     * @var int
60
     */
61
    private $pointUserId;
62
63
64
    public function __construct(MessageSender $messageSender, UserApi $userApi, AccountFactory $accountFactory, EntityManagerInterface $em, int $pointUserId)
65
    {
66
        $this->messenger = $messageSender;
67
        $this->userApi = $userApi;
68
        $this->accountFactory = $accountFactory;
69
        $this->em = $em;
70
        $this->pointUserId = $pointUserId;
71
72
        $this->userRepo = $em->getRepository('SkobkinPointToolsBundle:User');
73
        $this->subscriptionRepo = $em->getRepository('SkobkinPointToolsBundle:Subscription');
74
        $this->subscriptionEventRepo = $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent');
75
    }
76
77
    public function process(Message $message)
78
    {
79
        if (!IncomingUpdateDispatcher::CHAT_TYPE_PRIVATE === $message->chat->type) {
80
            throw new \LogicException('This service can process only private chat messages');
81
        }
82
83
        try {
84
            // Registering Telegram user
85
            /** @var Account $account */
86
            $account = $this->accountFactory->findOrCreateFromMessage($message);
87
            $this->em->flush();
88
        } catch (\Exception $e) {
89
            // Low-level message in case of incorrect $account
90
            $this->messenger->sendMessageToChat($message->chat->id, 'There was an error during your Telegram account registration. Try again or report the bug.');
91
        }
92
93
        try {
94
            $words = explode(' ', $message->text, 10);
95
96
            if (0 === count($words)) {
97
                return;
98
            }
99
100
            switch ($words[0]) {
101
                case '/link':
102
                case 'link':
103
                    $this->processLink($account, $words);
104
                    break;
105
106
                case '/me':
107
                case 'me':
108
                    $this->processMe($account);
109
                    break;
110
111
                case '/last':
112
                case 'last':
113
                    $this->processLast($account, $words);
114
                    break;
115
116
                case '/sub':
117
                case 'sub':
118
                    $this->processSub($account, $words);
119
                    break;
120
121
                case '/stats':
122
                case 'stats':
123
                    $this->processStats($account);
124
                    break;
125
126
                // Settings menu
127
                case '/set':
128
                    $this->processSet($account, $words);
129
                    break;
130
131
                // Exit from any menu and remove keyboard
132
                case '/exit':
133
                case 'exit':
134
                    $this->processExit($account);
135
                    break;
136
137
                case '/help':
138
                default:
139
                    $this->processHelp($account);
140
                    break;
141
            }
142
        } catch (CommandProcessingException $e) {
143
            $this->sendError($account, 'Processing error', $e->getMessage());
144
145
            if ($e->getPrevious()) {
146
                throw $e->getPrevious();
147
            }
148
        } catch (\Exception $e) {
149
            $this->sendError($account, 'Unknown error');
150
151
            throw $e;
152
        }
153
    }
154
155
    private function linkAccount(Account $account, string $login, string $password): bool
156
    {
157
        /** @var User $user */
158
        if (null === $user = $this->userRepo->findUserByLogin($login)) {
159
            throw new CommandProcessingException('User not found in Point Tools database. Please try again later.');
160
        }
161
162
        if ($this->userApi->isAuthDataValid($login, $password)) {
163
            $account->setUser($user);
164
165
            return true;
166
        }
167
168
        return false;
169
    }
170
171
    private function processLink(Account $account, array $words)
172
    {
173
        if (array_key_exists(2, $words)) {
174
            if ($this->linkAccount($account, $words[1], $words[2])) {
175
                // Saving linking status
176
                $this->em->flush();
177
                $this->sendAccountLinked($account);
178
            } else {
179
                $this->sendError($account, 'Account linking error', 'Check login and password or try again later.');
180
            }
181
        } else {
182
            $this->sendError($account, 'Login/Password error', 'You need to specify login and password separated by space after /link (example: `/link mylogin MypASSw0rd`)');
183
        }
184
    }
185
186
    private function processMe(Account $account)
187
    {
188
        if ($user = $account->getUser()) {
189
            $this->sendUserEvents($account, $user);
190
        } else {
191
            $this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.');
192
        }
193
    }
194
195
    private function processLast(Account $account, array $words)
196
    {
197
        if (array_key_exists(1, $words)) {
198 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...
199
                $this->sendUserEvents($account, $user);
200
            } else {
201
                $this->sendError($account, 'User not found');
202
            }
203
        } else {
204
            $this->sendGlobalEvents($account);
205
        }
206
    }
207
208
    private function processSub(Account $account, array $words)
209
    {
210
        if (array_key_exists(1, $words)) {
211 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...
212
                $this->sendUserSubscribers($account, $user);
213
            } else {
214
                $this->sendError($account, 'User not found');
215
            }
216
        } else {
217
            if ($user = $account->getUser()) {
218
                $this->sendUserSubscribers($account, $user);
219
            } else {
220
                $this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.');
221
            }
222
        }
223
    }
224
225
    private function processStats(Account $account)
226
    {
227
        $this->sendStats($account);
228
    }
229
230
    private function processSet(Account $account, array $words)
231
    {
232
        $keyboard = new ReplyKeyboardMarkup();
233
234
        if (array_key_exists(1, $words)) {
235
            if (array_key_exists(2, $words)) {
236
                if ('renames' === $words[2]) {
237
                    $account->toggleRenameNotification();
238
                    $this->em->flush();
239
240
                    $this->messenger->sendMessage($account, 'Renaming notifications are turned '.($account->isRenameNotification() ? 'on' : 'off'));
241
                } elseif ('subscribers' === $words[2]) {
242
                    $account->toggleSubscriberNotification();
243
                    $this->em->flush();
244
245
                    $this->messenger->sendMessage($account, 'Subscribers notifications are turned '.($account->isSubscriberNotification() ? 'on' : 'off'));
246
247
                    if ($account->isSubscriberNotification() && null === $account->getUser()) {
248
                        $this->messenger->sendMessage($account, 'You need to /link you account to receive these notifications.');
249
                    }
250
                } else {
251
                    $this->sendError($account, 'Notification type does not exist.');
252
                }
253
            } else {
1 ignored issue
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...
254
                $keyboard->keyboard = [
255
                    ['/set notifications renames'],
256
                    ['/set notifications subscribers'],
257
                    ['exit'],
258
                ];
259
260
                $this->messenger->sendMessage($account, 'Choose which notification type to toggle', MessageSender::PARSE_PLAIN, $keyboard);
261
            }
262
263
        } else {
1 ignored issue
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...
264
            $keyboard->keyboard = [
265
                ['/set notifications'],
266
                ['exit'],
267
            ];
268
269
            $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard);
270
        }
271
    }
272
273
    /**
274
     * Processes exit from keyboard menus and removes the keyboard
275
     */
276
    private function processExit(Account $account)
277
    {
278
        $keyboardRemove = new ReplyKeyboardRemove();
279
280
        $this->messenger->sendMessage($account, 'Done', MessageSender::PARSE_PLAIN, $keyboardRemove);
281
    }
282
283
    private function processHelp(Account $account)
284
    {
285
        $this->sendHelp($account);
286
    }
287
288
    private function sendAccountLinked(Account $account)
289
    {
290
        $this->messenger->sendMessage($account, 'Account linked. Try using /me now.');
291
    }
292
293
    private function sendUserSubscribers(Account $account, User $user)
294
    {
295
        $subscribers = [];
296
        foreach ($user->getSubscribers() as $subscription) {
297
            $subscribers[] = '@'.$subscription->getSubscriber()->getLogin();
298
        }
299
300
        $this->messenger->sendTemplatedMessage(
301
            $account,
302
            '@SkobkinPointTools/Telegram/user_subscribers.md.twig',
303
            [
304
                'user' => $user,
305
                'subscribers' => $subscribers,
306
            ]
307
        );
308
    }
309
310
    private function sendUserEvents(Account $account, User $user)
311
    {
312
        $events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10);
313
314
        $this->messenger->sendTemplatedMessage(
315
            $account,
316
            '@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig',
317
            [
318
                'user' => $user,
319
                'events' => $events,
320
            ]
321
        );
322
    }
323
324
    private function sendGlobalEvents(Account $account)
325
    {
326
        $events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10);
327
328
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]);
329
    }
330
331
    private function sendStats(Account $account)
332
    {
333
        $this->messenger->sendTemplatedMessage(
334
            $account,
335
            '@SkobkinPointTools/Telegram/stats.md.twig',
336
            [
337
                'total_users' => $this->userRepo->getUsersCount(),
338
                'active_users' => $this->subscriptionRepo->getUserSubscribersCountById($this->pointUserId),
339
                'today_events' => $this->subscriptionEventRepo->getLastDayEventsCount(),
340
            ]
341
        );
342
    }
343
344
    private function sendHelp(Account $account)
345
    {
346
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig');
347
    }
348
349
    private function sendError(Account $account, string $title, string $text = '')
350
    {
351
        $this->messenger->sendTemplatedMessage(
352
            $account,
353
            '@SkobkinPointTools/Telegram/error.md.twig',
354
            [
355
                'title' => $title,
356
                'text' => $text,
357
            ]
358
        );
359
    }
360
361
362
}