zend-blog-3-backend
273 строки · 6.9 Кб
1<?php
2/**
3* Created by PhpStorm.
4* User: morontt
5* Date: 24.09.17
6* Time: 10:29
7*/
8
9namespace Xelbot\Telegram;
10
11use Psr\Log\LoggerInterface;
12use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
13use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
14use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
15use Symfony\Component\Serializer\Serializer;
16use Xelbot\Telegram\Command\AbstractAdminCommand;
17use Xelbot\Telegram\Command\TelegramCommandInterface;
18use Xelbot\Telegram\Entity\Message;
19use Xelbot\Telegram\Entity\Update;
20use Xelbot\Telegram\Exception\AccessDeniedTelegramException;
21use Xelbot\Telegram\Exception\TelegramException;
22
23class Robot
24{
25public const EMOJI_ROBOT = '🤖';
26public const EMOJI_THINKING_FACE = '🤔';
27
28protected string $token = '';
29
30protected string $botName = '';
31
32protected int $adminId;
33
34/**
35* @var TelegramRequester
36*/
37protected $requester;
38
39/**
40* @var LoggerInterface|null
41*/
42protected $logger = null;
43
44/**
45* @var TelegramCommandInterface[]
46*/
47protected $commands = [];
48
49/**
50* @var UpdatesManagerInterface|null
51*/
52private $updatesManager;
53
54/**
55* @param string $token
56* @param string $botName
57* @param int $adminId
58*
59* @throws TelegramException
60*/
61public function __construct(string $token, string $botName, int $adminId)
62{
63if (!$token) {
64throw new TelegramException('API KEY not defined');
65}
66
67if (!preg_match('/\d+\:[\w\-]+/', $token)) {
68throw new TelegramException('Invalid API KEY');
69}
70
71$this->token = $token;
72$this->adminId = $adminId;
73
74if ($botName) {
75$this->botName = $botName;
76}
77
78$this->requester = new TelegramRequester($token);
79}
80
81/**
82* @param LoggerInterface|null $logger
83*/
84public function setLogger(LoggerInterface $logger = null): void
85{
86$this->logger = $logger;
87$this->requester->setLogger($logger);
88}
89
90/**
91* @param UpdatesManagerInterface $updatesManager
92*/
93public function setUpdatesManager(UpdatesManagerInterface $updatesManager): void
94{
95$this->updatesManager = $updatesManager;
96}
97
98/**
99* @param TelegramCommandInterface $command
100*/
101public function addCommand(TelegramCommandInterface $command): void
102{
103$command->setRequester($this->requester);
104
105if ($command instanceof AbstractAdminCommand) {
106$command->setAdminId($this->adminId);
107}
108
109$this->commands[$command->getCommandName()] = $command;
110}
111
112/**
113* @param string $message
114* @param int|null $chatId
115*
116* @return TelegramResponse
117*/
118public function sendMessage(string $message, int $chatId = null): TelegramResponse
119{
120if ($chatId === null) {
121$chatId = $this->adminId;
122}
123
124return $this->requester->sendMessage([
125'chat_id' => $chatId,
126'text' => $message,
127]);
128}
129
130/**
131* @param string $url
132* @param string|null $certificate
133*
134* @throws TelegramException
135*
136* @return TelegramResponse
137*/
138public function setWebhook(string $url, string $certificate = null): TelegramResponse
139{
140$data = [
141'url' => $url,
142];
143
144if ($certificate) {
145$data['certificate'] = $this->getResource($certificate);
146}
147
148return $this->requester->setWebhook($data);
149}
150
151/**
152* @return TelegramResponse
153*/
154public function getWebhookInfo(): TelegramResponse
155{
156return $this->requester->getWebhookInfo();
157}
158
159/**
160* @return TelegramResponse
161*/
162public function deleteWebhook(): TelegramResponse
163{
164return $this->requester->deleteWebhook();
165}
166
167/**
168* @param array $requestData
169*
170* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
171*/
172public function handle(array $requestData): void
173{
174if ($this->logger) {
175$this->logger->info('Webhook: ', $requestData);
176}
177
178$normalizer = new ObjectNormalizer(
179null,
180new CamelCaseToSnakeCaseNameConverter(),
181null,
182new ReflectionExtractor()
183);
184
185$serializer = new Serializer([$normalizer]);
186$obj = $serializer->denormalize($requestData, Update::class);
187
188if ($this->updatesManager) {
189$this->updatesManager->saveUpdate($obj, $requestData);
190}
191
192if ($message = $obj->getMessage()) {
193foreach ($message->getEntities() as $entity) {
194if ($entity['type'] === 'bot_command') {
195$this->executeCommand($message, $entity);
196}
197}
198}
199}
200
201/**
202* @param string $file
203*
204* @throws TelegramException
205*
206* @return resource
207*/
208protected function getResource(string $file)
209{
210$fp = fopen($file, 'rb');
211if ($fp === false) {
212throw new TelegramException('Cannot open ' . $file);
213}
214
215return $fp;
216}
217
218/**
219* @param Message $message
220* @param array $entity
221*/
222protected function executeCommand(Message $message, array $entity): void
223{
224if (!$message->getChat()) {
225$this->logger->error('message without chat', ['message' => serialize($message)]);
226
227return;
228}
229
230$commandName = mb_substr($message->getText(), $entity['offset'] + 1, $entity['length'] - 1);
231try {
232if (isset($this->commands[$commandName])) {
233$this->commands[$commandName]->execute($message);
234} else {
235//TODO Null pointer exception may occur here
236$this->requester->sendMessage([
237'chat_id' => $message->getChat()->getId(),
238'text' => sprintf(
239'Не знаю такую команду%s %s ',
240$this->appealTo($message->getFrom()->getId()),
241self::EMOJI_ROBOT
242),
243'parse_mode' => 'HTML',
244]);
245}
246} catch (AccessDeniedTelegramException $e) {
247$this->requester->sendMessage([
248'chat_id' => $message->getChat()->getId(),
249'text' => 'Кто вы? ' . self::EMOJI_THINKING_FACE,
250'parse_mode' => 'HTML',
251]);
252
253$this->logger->notice($e->getMessage());
254} catch (\Throwable $e) {
255$this->logger->error($e->getMessage());
256}
257}
258
259/**
260* @param int $idFrom
261*
262* @return string
263*/
264private function appealTo(int $idFrom): string
265{
266$text = '';
267if ($idFrom === $this->adminId) {
268$text = ', хозяин';
269}
270
271return $text;
272}
273}
274