|
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
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* Processes all private messages |
|
19
|
|
|
*/ |
|
20
|
|
|
class PrivateMessageProcessor |
|
21
|
|
|
{ |
|
22
|
|
|
/** |
|
23
|
|
|
* @var MessageSender |
|
24
|
|
|
*/ |
|
25
|
|
|
private $messenger; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* @var UserApi |
|
29
|
|
|
*/ |
|
30
|
|
|
private $userApi; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* @var AccountFactory |
|
34
|
|
|
*/ |
|
35
|
|
|
private $accountFactory; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* @var EntityManagerInterface |
|
39
|
|
|
*/ |
|
40
|
|
|
private $em; |
|
41
|
|
|
|
|
42
|
|
|
/** |
|
43
|
|
|
* @var \Twig_Environment |
|
44
|
|
|
*/ |
|
45
|
|
|
private $twig; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* @var UserRepository |
|
49
|
|
|
*/ |
|
50
|
|
|
private $userRepo; |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* @var SubscriptionRepository |
|
54
|
|
|
*/ |
|
55
|
|
|
private $subscriptionRepo; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* @var SubscriptionEventRepository |
|
59
|
|
|
*/ |
|
60
|
|
|
private $subscriptionEventRepo; |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* @var int |
|
64
|
|
|
*/ |
|
65
|
|
|
private $pointUserId; |
|
66
|
|
|
|
|
67
|
|
|
|
|
68
|
|
|
public function __construct( |
|
69
|
|
|
MessageSender $messageSender, |
|
70
|
|
|
UserApi $userApi, |
|
71
|
|
|
AccountFactory $accountFactory, |
|
72
|
|
|
EntityManagerInterface $em, |
|
73
|
|
|
\Twig_Environment $twig, |
|
74
|
|
|
int $pointUserId |
|
75
|
|
|
) |
|
76
|
|
|
{ |
|
77
|
|
|
$this->messenger = $messageSender; |
|
78
|
|
|
$this->userApi = $userApi; |
|
79
|
|
|
$this->accountFactory = $accountFactory; |
|
80
|
|
|
$this->em = $em; |
|
81
|
|
|
$this->twig = $twig; |
|
82
|
|
|
$this->pointUserId = $pointUserId; |
|
83
|
|
|
|
|
84
|
|
|
$this->userRepo = $em->getRepository('SkobkinPointToolsBundle:User'); |
|
85
|
|
|
$this->subscriptionRepo = $em->getRepository('SkobkinPointToolsBundle:Subscription'); |
|
86
|
|
|
$this->subscriptionEventRepo = $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent'); |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
public function process(Message $message) |
|
90
|
|
|
{ |
|
91
|
|
|
if (!IncomingUpdateDispatcher::CHAT_TYPE_PRIVATE === $message->chat->type) { |
|
92
|
|
|
throw new \LogicException('This service can process only private chat messages'); |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
try { |
|
96
|
|
|
// Registering Telegram user |
|
97
|
|
|
/** @var Account $account */ |
|
98
|
|
|
$account = $this->accountFactory->findOrCreateFromMessage($message); |
|
99
|
|
|
$this->em->flush(); |
|
100
|
|
|
} catch (\Exception $e) { |
|
101
|
|
|
// Low-level message in case of incorrect $account |
|
102
|
|
|
$this->messenger->sendMessageToChat($message->chat->id, 'There was an error during your Telegram account registration. Try again or report the bug.'); |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
try { |
|
106
|
|
|
$words = explode(' ', $message->text, 10); |
|
107
|
|
|
|
|
108
|
|
|
if (0 === count($words)) { |
|
109
|
|
|
return; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
switch ($words[0]) { |
|
113
|
|
|
case '/link': |
|
114
|
|
|
case 'link': |
|
115
|
|
|
if (array_key_exists(2, $words)) { |
|
116
|
|
|
if ($this->linkAccount($account, $words[1], $words[2])) { |
|
117
|
|
|
// Saving linking status |
|
118
|
|
|
$this->em->flush(); |
|
119
|
|
|
$this->sendAccountLinked($account); |
|
120
|
|
|
} else { |
|
121
|
|
|
$this->sendError($account, 'Account linking error', 'Check login and password or try again later.'); |
|
122
|
|
|
} |
|
123
|
|
|
} else { |
|
124
|
|
|
$this->sendError($account, 'Login/Password error', 'You need to specify login and password separated by space after /link (example: `/link mylogin MypASSw0rd`)'); |
|
125
|
|
|
} |
|
126
|
|
|
break; |
|
127
|
|
|
|
|
128
|
|
|
case '/me': |
|
129
|
|
|
case 'me': |
|
130
|
|
|
if ($user = $account->getUser()) { |
|
131
|
|
|
$this->sendUserEvents($account, $user); |
|
132
|
|
|
} else { |
|
133
|
|
|
$this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.'); |
|
134
|
|
|
} |
|
135
|
|
|
break; |
|
136
|
|
|
|
|
137
|
|
|
case '/last': |
|
138
|
|
|
case 'last': |
|
139
|
|
|
if (array_key_exists(1, $words)) { |
|
140
|
|
View Code Duplication |
if (null !== $user = $this->userRepo->findUserByLogin($words[1])) { |
|
|
|
|
|
|
141
|
|
|
$this->sendUserEvents($account, $user); |
|
142
|
|
|
} else { |
|
143
|
|
|
$this->sendError($account, 'User not found'); |
|
144
|
|
|
} |
|
145
|
|
|
} else { |
|
146
|
|
|
$this->sendGlobalEvents($account); |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
break; |
|
150
|
|
|
|
|
151
|
|
|
case '/sub': |
|
152
|
|
|
case 'sub': |
|
153
|
|
|
if (array_key_exists(1, $words)) { |
|
154
|
|
View Code Duplication |
if (null !== $user = $this->userRepo->findUserByLogin($words[1])) { |
|
|
|
|
|
|
155
|
|
|
$this->sendUserSubscribers($account, $user); |
|
156
|
|
|
} else { |
|
157
|
|
|
$this->sendError($account, 'User not found'); |
|
158
|
|
|
} |
|
159
|
|
|
} else { |
|
160
|
|
|
if ($user = $account->getUser()) { |
|
161
|
|
|
$this->sendUserSubscribers($account, $user); |
|
162
|
|
|
} else { |
|
163
|
|
|
$this->sendError($account, 'Account not linked', 'You must /link your account first to be able to use this command.'); |
|
164
|
|
|
} |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
break; |
|
168
|
|
|
|
|
169
|
|
|
case '/stats': |
|
170
|
|
|
case 'stats': |
|
171
|
|
|
$this->sendStats($account); |
|
172
|
|
|
|
|
173
|
|
|
break; |
|
174
|
|
|
|
|
175
|
|
|
case '/help': |
|
176
|
|
|
default: |
|
177
|
|
|
$this->sendHelp($account); |
|
178
|
|
|
break; |
|
179
|
|
|
} |
|
180
|
|
|
} catch (CommandProcessingException $e) { |
|
181
|
|
|
$this->sendError($account, 'Processing error', $e->getMessage()); |
|
182
|
|
|
|
|
183
|
|
|
if ($e->getPrevious()) { |
|
184
|
|
|
throw $e->getPrevious(); |
|
185
|
|
|
} |
|
186
|
|
|
} catch (\Exception $e) { |
|
187
|
|
|
$this->sendError($account, 'Unknown error'); |
|
188
|
|
|
|
|
189
|
|
|
throw $e; |
|
190
|
|
|
} |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
private function linkAccount(Account $account, string $login, string $password): bool |
|
194
|
|
|
{ |
|
195
|
|
|
/** @var User $user */ |
|
196
|
|
|
if (null === $user = $this->userRepo->findUserByLogin($login)) { |
|
197
|
|
|
throw new CommandProcessingException('User not found in Point Tools database. Please try again later.'); |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
if ($this->userApi->isAuthDataValid($login, $password)) { |
|
201
|
|
|
$account->setUser($user); |
|
202
|
|
|
|
|
203
|
|
|
return true; |
|
204
|
|
|
} |
|
205
|
|
|
|
|
206
|
|
|
return false; |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
private function sendAccountLinked(Account $account) |
|
210
|
|
|
{ |
|
211
|
|
|
$this->messenger->sendMessageToUser($account, 'Account linked. Try using /me now.'); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
private function sendUserSubscribers(Account $account, User $user) |
|
215
|
|
|
{ |
|
216
|
|
|
$subscribers = []; |
|
217
|
|
|
foreach ($user->getSubscribers() as $subscription) { |
|
218
|
|
|
$subscribers[] = '@'.$subscription->getSubscriber()->getLogin(); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
$this->sendTemplatedMessage( |
|
222
|
|
|
$account, |
|
223
|
|
|
'@SkobkinPointTools/Telegram/user_subscribers.md.twig', |
|
224
|
|
|
[ |
|
225
|
|
|
'user' => $user, |
|
226
|
|
|
'subscribers' => $subscribers, |
|
227
|
|
|
] |
|
228
|
|
|
); |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
private function sendUserEvents(Account $account, User $user) |
|
232
|
|
|
{ |
|
233
|
|
|
$events = $this->subscriptionEventRepo->getUserLastSubscribersEvents($user, 10); |
|
234
|
|
|
|
|
235
|
|
|
$this->sendTemplatedMessage( |
|
236
|
|
|
$account, |
|
237
|
|
|
'@SkobkinPointTools/Telegram/last_user_subscriptions.md.twig', |
|
238
|
|
|
[ |
|
239
|
|
|
'user' => $user, |
|
240
|
|
|
'events' => $events, |
|
241
|
|
|
] |
|
242
|
|
|
); |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
private function sendGlobalEvents(Account $account) |
|
246
|
|
|
{ |
|
247
|
|
|
$events = $this->subscriptionEventRepo->getLastSubscriptionEvents(10); |
|
248
|
|
|
|
|
249
|
|
|
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/last_global_subscriptions.md.twig', ['events' => $events]); |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
private function sendStats(Account $account) |
|
253
|
|
|
{ |
|
254
|
|
|
$this->sendTemplatedMessage( |
|
255
|
|
|
$account, |
|
256
|
|
|
'@SkobkinPointTools/Telegram/stats.md.twig', |
|
257
|
|
|
[ |
|
258
|
|
|
'total_users' => $this->userRepo->getUsersCount(), |
|
259
|
|
|
'active_users' => $this->subscriptionRepo->getUserSubscribersCountById($this->pointUserId), |
|
260
|
|
|
'today_events' => $this->subscriptionEventRepo->getLastDayEventsCount(), |
|
261
|
|
|
] |
|
262
|
|
|
); |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
private function sendHelp(Account $account) |
|
266
|
|
|
{ |
|
267
|
|
|
$this->sendTemplatedMessage($account, '@SkobkinPointTools/Telegram/help.md.twig'); |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
private function sendError(Account $account, string $title, string $text = '') |
|
271
|
|
|
{ |
|
272
|
|
|
$this->sendTemplatedMessage( |
|
273
|
|
|
$account, |
|
274
|
|
|
'@SkobkinPointTools/Telegram/error.md.twig', |
|
275
|
|
|
[ |
|
276
|
|
|
'title' => $title, |
|
277
|
|
|
'text' => $text, |
|
278
|
|
|
] |
|
279
|
|
|
); |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
private function sendTemplatedMessage( |
|
283
|
|
|
Account $account, |
|
284
|
|
|
string $template, |
|
285
|
|
|
array $templateData = [], |
|
286
|
|
|
ReplyKeyboardMarkup $keyboardMarkup = null, |
|
287
|
|
|
bool $disableWebPreview = true, |
|
288
|
|
|
string $format = MessageSender::PARSE_MODE_MARKDOWN |
|
289
|
|
|
): bool |
|
290
|
|
|
{ |
|
291
|
|
|
$text = $this->twig->render($template, $templateData); |
|
292
|
|
|
|
|
293
|
|
|
return $this->messenger->sendMessageToUser($account, $text, $format, $keyboardMarkup, $disableWebPreview, false); |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
private function sendPlainTextMessage(Account $account, string $text, ReplyKeyboardMarkup $keyboardMarkup = null, bool $disableWebPreview = true): bool |
|
|
|
|
|
|
297
|
|
|
{ |
|
298
|
|
|
return $this->messenger->sendMessageToUser($account, $text, MessageSender::PARSE_MODE_NOPARSE, $keyboardMarkup, $disableWebPreview); |
|
299
|
|
|
} |
|
300
|
|
|
} |
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.