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