|
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\Abstracts\KeyboardMethods; |
|
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 \Twig_Environment |
|
46
|
|
|
*/ |
|
47
|
|
|
private $twig; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var UserRepository |
|
51
|
|
|
*/ |
|
52
|
|
|
private $userRepo; |
|
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
|
|
|
MessageSender $messageSender, |
|
72
|
|
|
UserApi $userApi, |
|
73
|
|
|
AccountFactory $accountFactory, |
|
74
|
|
|
EntityManagerInterface $em, |
|
75
|
|
|
\Twig_Environment $twig, |
|
76
|
|
|
int $pointUserId |
|
77
|
|
|
) |
|
78
|
|
|
{ |
|
79
|
|
|
$this->messenger = $messageSender; |
|
80
|
|
|
$this->userApi = $userApi; |
|
81
|
|
|
$this->accountFactory = $accountFactory; |
|
82
|
|
|
$this->em = $em; |
|
83
|
|
|
$this->twig = $twig; |
|
84
|
|
|
$this->pointUserId = $pointUserId; |
|
85
|
|
|
|
|
86
|
|
|
$this->userRepo = $em->getRepository('SkobkinPointToolsBundle:User'); |
|
87
|
|
|
$this->subscriptionRepo = $em->getRepository('SkobkinPointToolsBundle:Subscription'); |
|
88
|
|
|
$this->subscriptionEventRepo = $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent'); |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
public function process(Message $message) |
|
92
|
|
|
{ |
|
93
|
|
|
if (!IncomingUpdateDispatcher::CHAT_TYPE_PRIVATE === $message->chat->type) { |
|
94
|
|
|
throw new \LogicException('This service can process only private chat messages'); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
try { |
|
98
|
|
|
// Registering Telegram user |
|
99
|
|
|
/** @var Account $account */ |
|
100
|
|
|
$account = $this->accountFactory->findOrCreateFromMessage($message); |
|
101
|
|
|
$this->em->flush(); |
|
102
|
|
|
} catch (\Exception $e) { |
|
103
|
|
|
// Low-level message in case of incorrect $account |
|
104
|
|
|
$this->messenger->sendMessageToChat($message->chat->id, 'There was an error during your Telegram account registration. Try again or report the bug.'); |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
try { |
|
108
|
|
|
$words = explode(' ', $message->text, 10); |
|
109
|
|
|
|
|
110
|
|
|
if (0 === count($words)) { |
|
111
|
|
|
return; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
switch ($words[0]) { |
|
115
|
|
|
case '/link': |
|
116
|
|
|
case 'link': |
|
117
|
|
|
$this->processLink($account, $words); |
|
118
|
|
|
break; |
|
119
|
|
|
|
|
120
|
|
|
case '/me': |
|
121
|
|
|
case 'me': |
|
122
|
|
|
$this->processMe($account); |
|
123
|
|
|
break; |
|
124
|
|
|
|
|
125
|
|
|
case '/last': |
|
126
|
|
|
case 'last': |
|
127
|
|
|
$this->processLast($account, $words); |
|
128
|
|
|
break; |
|
129
|
|
|
|
|
130
|
|
|
case '/sub': |
|
131
|
|
|
case 'sub': |
|
132
|
|
|
$this->processSub($account, $words); |
|
133
|
|
|
break; |
|
134
|
|
|
|
|
135
|
|
|
case '/stats': |
|
136
|
|
|
case 'stats': |
|
137
|
|
|
$this->processStats($account); |
|
138
|
|
|
break; |
|
139
|
|
|
|
|
140
|
|
|
// Settings menu |
|
141
|
|
|
case '/set': |
|
142
|
|
|
$this->processSet($account, $words); |
|
143
|
|
|
break; |
|
144
|
|
|
|
|
145
|
|
|
// Exit from any menu and remove keyboard |
|
146
|
|
|
case '/exit': |
|
147
|
|
|
case 'exit': |
|
148
|
|
|
$this->processExit($account); |
|
149
|
|
|
break; |
|
150
|
|
|
|
|
151
|
|
|
case '/help': |
|
152
|
|
|
default: |
|
153
|
|
|
$this->processHelp($account); |
|
154
|
|
|
break; |
|
155
|
|
|
} |
|
156
|
|
|
} catch (CommandProcessingException $e) { |
|
157
|
|
|
$this->sendError($account, 'Processing error', $e->getMessage()); |
|
158
|
|
|
|
|
159
|
|
|
if ($e->getPrevious()) { |
|
160
|
|
|
throw $e->getPrevious(); |
|
161
|
|
|
} |
|
162
|
|
|
} catch (\Exception $e) { |
|
163
|
|
|
$this->sendError($account, 'Unknown error'); |
|
164
|
|
|
|
|
165
|
|
|
throw $e; |
|
166
|
|
|
} |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
private function linkAccount(Account $account, string $login, string $password): bool |
|
170
|
|
|
{ |
|
171
|
|
|
/** @var User $user */ |
|
172
|
|
|
if (null === $user = $this->userRepo->findUserByLogin($login)) { |
|
173
|
|
|
throw new CommandProcessingException('User not found in Point Tools database. Please try again later.'); |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
if ($this->userApi->isAuthDataValid($login, $password)) { |
|
177
|
|
|
$account->setUser($user); |
|
178
|
|
|
|
|
179
|
|
|
return true; |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
return false; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
private function processLink(Account $account, array $words) |
|
186
|
|
|
{ |
|
187
|
|
|
if (array_key_exists(2, $words)) { |
|
188
|
|
|
if ($this->linkAccount($account, $words[1], $words[2])) { |
|
189
|
|
|
// Saving linking status |
|
190
|
|
|
$this->em->flush(); |
|
191
|
|
|
$this->sendAccountLinked($account); |
|
192
|
|
|
} else { |
|
193
|
|
|
$this->sendError($account, 'Account linking error', 'Check login and password or try again later.'); |
|
194
|
|
|
} |
|
195
|
|
|
} else { |
|
196
|
|
|
$this->sendError($account, 'Login/Password error', 'You need to specify login and password separated by space after /link (example: `/link mylogin MypASSw0rd`)'); |
|
197
|
|
|
} |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
private function processMe(Account $account) |
|
201
|
|
|
{ |
|
202
|
|
|
if ($user = $account->getUser()) { |
|
203
|
|
|
$this->sendUserEvents($account, $user); |
|
204
|
|
|
} else { |
|
205
|
|
|
$this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.'); |
|
206
|
|
|
} |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
private function processLast(Account $account, array $words) |
|
210
|
|
|
{ |
|
211
|
|
|
if (array_key_exists(1, $words)) { |
|
212
|
|
View Code Duplication |
if (null !== $user = $this->userRepo->findUserByLogin($words[1])) { |
|
|
|
|
|
|
213
|
|
|
$this->sendUserEvents($account, $user); |
|
214
|
|
|
} else { |
|
215
|
|
|
$this->sendError($account, 'User not found'); |
|
216
|
|
|
} |
|
217
|
|
|
} else { |
|
218
|
|
|
$this->sendGlobalEvents($account); |
|
219
|
|
|
} |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
private function processSub(Account $account, array $words) |
|
223
|
|
|
{ |
|
224
|
|
|
if (array_key_exists(1, $words)) { |
|
225
|
|
View Code Duplication |
if (null !== $user = $this->userRepo->findUserByLogin($words[1])) { |
|
|
|
|
|
|
226
|
|
|
$this->sendUserSubscribers($account, $user); |
|
227
|
|
|
} else { |
|
228
|
|
|
$this->sendError($account, 'User not found'); |
|
229
|
|
|
} |
|
230
|
|
|
} else { |
|
231
|
|
|
if ($user = $account->getUser()) { |
|
232
|
|
|
$this->sendUserSubscribers($account, $user); |
|
233
|
|
|
} else { |
|
234
|
|
|
$this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.'); |
|
235
|
|
|
} |
|
236
|
|
|
} |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
private function processStats(Account $account) |
|
240
|
|
|
{ |
|
241
|
|
|
$this->sendStats($account); |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
private function processSet(Account $account, array $words) |
|
245
|
|
|
{ |
|
246
|
|
|
$keyboard = new ReplyKeyboardMarkup(); |
|
247
|
|
|
|
|
248
|
|
|
if (array_key_exists(1, $words)) { |
|
249
|
|
|
if (array_key_exists(2, $words)) { |
|
250
|
|
|
if ('renames' === $words[2]) { |
|
251
|
|
|
$account->toggleRenameNotification(); |
|
252
|
|
|
$this->em->flush(); |
|
253
|
|
|
|
|
254
|
|
|
$this->sendPlainTextMessage($account, 'Renaming notifications are turned '.($account->isRenameNotification() ? 'on' : 'off')); |
|
255
|
|
|
} elseif ('subscribers' === $words[2]) { |
|
256
|
|
|
$account->toggleSubscriberNotification(); |
|
257
|
|
|
$this->em->flush(); |
|
258
|
|
|
|
|
259
|
|
|
$this->sendPlainTextMessage($account, 'Subscribers notifications are turned '.($account->isSubscriberNotification() ? 'on' : 'off')); |
|
260
|
|
|
} else { |
|
261
|
|
|
$this->sendError($account, 'Notification type does not exist.'); |
|
262
|
|
|
} |
|
263
|
|
View Code Duplication |
} else { |
|
|
|
|
|
|
264
|
|
|
$keyboard->keyboard = [ |
|
265
|
|
|
['/set notifications renames'], |
|
266
|
|
|
['/set notifications subscribers'], |
|
267
|
|
|
['exit'], |
|
268
|
|
|
]; |
|
269
|
|
|
|
|
270
|
|
|
$this->sendPlainTextMessage($account, 'Choose which notification type to toggle', $keyboard); |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
View Code Duplication |
} else { |
|
|
|
|
|
|
274
|
|
|
$keyboard->keyboard = [ |
|
275
|
|
|
['/set notifications'], |
|
276
|
|
|
['exit'], |
|
277
|
|
|
]; |
|
278
|
|
|
|
|
279
|
|
|
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/settings.md.twig', ['account' => $account], $keyboard); |
|
280
|
|
|
} |
|
281
|
|
|
} |
|
282
|
|
|
|
|
283
|
|
|
/** |
|
284
|
|
|
* Processes exit from keyboard menus and removes the keyboard |
|
285
|
|
|
*/ |
|
286
|
|
|
private function processExit(Account $account) |
|
287
|
|
|
{ |
|
288
|
|
|
$keyboardRemove = new ReplyKeyboardRemove(); |
|
289
|
|
|
|
|
290
|
|
|
$this->sendPlainTextMessage($account, 'Done', $keyboardRemove); |
|
291
|
|
|
} |
|
292
|
|
|
|
|
293
|
|
|
private function processHelp(Account $account) |
|
294
|
|
|
{ |
|
295
|
|
|
$this->sendHelp($account); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
private function sendAccountLinked(Account $account) |
|
299
|
|
|
{ |
|
300
|
|
|
$this->messenger->sendMessageToUser($account, 'Account linked. Try using /me now.'); |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
private function sendUserSubscribers(Account $account, User $user) |
|
304
|
|
|
{ |
|
305
|
|
|
$subscribers = []; |
|
306
|
|
|
foreach ($user->getSubscribers() as $subscription) { |
|
307
|
|
|
$subscribers[] = '@'.$subscription->getSubscriber()->getLogin(); |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
$this->sendTemplatedMessage( |
|
311
|
|
|
$account, |
|
312
|
|
|
'@SkobkinPointTools/Telegram/user_subscribers.md.twig', |
|
313
|
|
|
[ |
|
314
|
|
|
'user' => $user, |
|
315
|
|
|
'subscribers' => $subscribers, |
|
316
|
|
|
] |
|
317
|
|
|
); |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
private function sendUserEvents(Account $account, User $user) |
|
321
|
|
|
{ |
|
322
|
|
|
$events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10); |
|
323
|
|
|
|
|
324
|
|
|
$this->sendTemplatedMessage( |
|
325
|
|
|
$account, |
|
326
|
|
|
'@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig', |
|
327
|
|
|
[ |
|
328
|
|
|
'user' => $user, |
|
329
|
|
|
'events' => $events, |
|
330
|
|
|
] |
|
331
|
|
|
); |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
private function sendGlobalEvents(Account $account) |
|
335
|
|
|
{ |
|
336
|
|
|
$events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10); |
|
337
|
|
|
|
|
338
|
|
|
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]); |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
private function sendStats(Account $account) |
|
342
|
|
|
{ |
|
343
|
|
|
$this->sendTemplatedMessage( |
|
344
|
|
|
$account, |
|
345
|
|
|
'@SkobkinPointTools/Telegram/stats.md.twig', |
|
346
|
|
|
[ |
|
347
|
|
|
'total_users' => $this->userRepo->getUsersCount(), |
|
348
|
|
|
'active_users' => $this->subscriptionRepo->getUserSubscribersCountById($this->pointUserId), |
|
349
|
|
|
'today_events' => $this->subscriptionEventRepo->getLastDayEventsCount(), |
|
350
|
|
|
] |
|
351
|
|
|
); |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
private function sendHelp(Account $account) |
|
355
|
|
|
{ |
|
356
|
|
|
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig'); |
|
357
|
|
|
} |
|
358
|
|
|
|
|
359
|
|
|
private function sendError(Account $account, string $title, string $text = '') |
|
360
|
|
|
{ |
|
361
|
|
|
$this->sendTemplatedMessage( |
|
362
|
|
|
$account, |
|
363
|
|
|
'@SkobkinPointTools/Telegram/error.md.twig', |
|
364
|
|
|
[ |
|
365
|
|
|
'title' => $title, |
|
366
|
|
|
'text' => $text, |
|
367
|
|
|
] |
|
368
|
|
|
); |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
private function sendTemplatedMessage( |
|
372
|
|
|
Account $account, |
|
373
|
|
|
string $template, |
|
374
|
|
|
array $templateData = [], |
|
375
|
|
|
KeyboardMethods $keyboardMarkup = null, |
|
376
|
|
|
bool $disableWebPreview = true, |
|
377
|
|
|
string $format = MessageSender::PARSE_MODE_MARKDOWN |
|
378
|
|
|
): bool |
|
379
|
|
|
{ |
|
380
|
|
|
$text = $this->twig->render($template, $templateData); |
|
381
|
|
|
|
|
382
|
|
|
return $this->messenger->sendMessageToUser($account, $text, $format, $keyboardMarkup, $disableWebPreview, false); |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
private function sendPlainTextMessage(Account $account, string $text, KeyboardMethods $keyboardMarkup = null, bool $disableWebPreview = true): bool |
|
386
|
|
|
{ |
|
387
|
|
|
return $this->messenger->sendMessageToUser($account, $text, MessageSender::PARSE_MODE_NOPARSE, $keyboardMarkup, $disableWebPreview); |
|
388
|
|
|
} |
|
389
|
|
|
} |
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.