Passed
Push — master ( 6c1980...4e24b5 )
by Alexey
03:34
created

PrivateMessageProcessor::sendTemplatedMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 6
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\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
                } else {
247
                    $this->sendError($account, 'Notification type does not exist.');
248
                }
249 View Code Duplication
            } 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...
250
                $keyboard->keyboard = [
251
                    ['/set notifications renames'],
252
                    ['/set notifications subscribers'],
253
                    ['exit'],
254
                ];
255
256
                $this->messenger->sendMessage($account, 'Choose which notification type to toggle', MessageSender::PARSE_PLAIN, $keyboard);
257
            }
258
259 View Code Duplication
        } 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...
260
            $keyboard->keyboard = [
261
                ['/set notifications'],
262
                ['exit'],
263
            ];
264
265
            $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard);
266
        }
267
    }
268
269
    /**
270
     * Processes exit from keyboard menus and removes the keyboard
271
     */
272
    private function processExit(Account $account)
273
    {
274
        $keyboardRemove = new ReplyKeyboardRemove();
275
276
        $this->messenger->sendMessage($account, 'Done', MessageSender::PARSE_PLAIN, $keyboardRemove);
277
    }
278
279
    private function processHelp(Account $account)
280
    {
281
        $this->sendHelp($account);
282
    }
283
284
    private function sendAccountLinked(Account $account)
285
    {
286
        $this->messenger->sendMessage($account, 'Account linked. Try using /me now.');
287
    }
288
289
    private function sendUserSubscribers(Account $account, User $user)
290
    {
291
        $subscribers = [];
292
        foreach ($user->getSubscribers() as $subscription) {
293
            $subscribers[] = '@'.$subscription->getSubscriber()->getLogin();
294
        }
295
296
        $this->messenger->sendTemplatedMessage(
297
            $account,
298
            '@SkobkinPointTools/Telegram/user_subscribers.md.twig',
299
            [
300
                'user' => $user,
301
                'subscribers' => $subscribers,
302
            ]
303
        );
304
    }
305
306
    private function sendUserEvents(Account $account, User $user)
307
    {
308
        $events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10);
309
310
        $this->messenger->sendTemplatedMessage(
311
            $account,
312
            '@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig',
313
            [
314
                'user' => $user,
315
                'events' => $events,
316
            ]
317
        );
318
    }
319
320
    private function sendGlobalEvents(Account $account)
321
    {
322
        $events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10);
323
324
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]);
325
    }
326
327
    private function sendStats(Account $account)
328
    {
329
        $this->messenger->sendTemplatedMessage(
330
            $account,
331
            '@SkobkinPointTools/Telegram/stats.md.twig',
332
            [
333
                'total_users' => $this->userRepo->getUsersCount(),
334
                'active_users' => $this->subscriptionRepo->getUserSubscribersCountById($this->pointUserId),
335
                'today_events' => $this->subscriptionEventRepo->getLastDayEventsCount(),
336
            ]
337
        );
338
    }
339
340
    private function sendHelp(Account $account)
341
    {
342
        $this->messenger->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig');
343
    }
344
345
    private function sendError(Account $account, string $title, string $text = '')
346
    {
347
        $this->messenger->sendTemplatedMessage(
348
            $account,
349
            '@SkobkinPointTools/Telegram/error.md.twig',
350
            [
351
                'title' => $title,
352
                'text' => $text,
353
            ]
354
        );
355
    }
356
357
358
}