Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
SendingService.php
1<?php
2
4
17use Bitrix\Im\V2\Common\ContextCustomer;
18use CIMMessageParamAttach;
19
21{
22 use ContextCustomer;
23
24 private SendingConfig $sendingConfig;
25
26 protected ?Uuid $uuidService;
27
28 public const
29 EVENT_BEFORE_MESSAGE_ADD = 'OnBeforeMessageAdd',//new
30 EVENT_AFTER_MESSAGE_ADD = 'OnAfterMessagesAdd',
31 EVENT_BEFORE_CHAT_MESSAGE_ADD = 'OnBeforeChatMessageAdd',
32 EVENT_BEFORE_NOTIFY_ADD = 'OnBeforeMessageNotifyAdd',
33 EVENT_AFTER_NOTIFY_ADD = 'OnBeforeMessageNotifyAdd'
34 ;
35
39 public function __construct(?SendingConfig $sendingConfig = null)
40 {
41 if ($sendingConfig === null)
42 {
43 $sendingConfig = new SendingConfig();
44 }
45 $this->sendingConfig = $sendingConfig;
46 }
47
48 //region UUID
49
54 public function checkDuplicateByUuid(Message $message): Result
55 {
56 $result = new Result;
57
58 if (
59 $message->getUuid()
60 && !$message->isSystem()
61 && Uuid::validate($message->getUuid())
62 )
63 {
64 $this->uuidService = new Uuid($message->getUuid());
65 $uuidAddResult = $this->uuidService->add();
66 // if it is false, then UUID already exists
67 if (!$uuidAddResult)
68 {
69 $messageIdByUuid = $this->uuidService->getMessageId();
70
71 // if we got message_id, then message already exists, and we don't need to add it, so return with ID.
72 if (!is_null($messageIdByUuid))
73 {
74 return $result->setResult(['messageId' => $messageIdByUuid]);
75 }
76
77 // if there is no message_id and entry date is expired,
78 // then update date_create and return false to delay next sending on the client.
79 if (!$this->uuidService->updateIfExpired())
80 {
81 return $result->addError(new MessageError(MessageError::MESSAGE_DUPLICATED_BY_UUID));
82 }
83 }
84 }
85
86 return $result;
87 }
88
93 public function updateMessageUuid(Message $message): void
94 {
95 if (isset($this->uuidService))
96 {
97 $this->uuidService->updateMessageId($message->getMessageId());
98 }
99 }
100
101 //endregion
102
103 //region Events
104
113 public function fireEventBeforeSend(Chat $chat, Message $message): Result
114 {
115 $result = new Result;
116
117 $event = new Event('im', static::EVENT_BEFORE_MESSAGE_ADD, [
118 'message' => $message->toArray(),
119 'parameters' => $this->sendingConfig->toArray(),
120 ]);
121 $event->send();
122
123 foreach ($event->getResults() as $eventResult)
124 {
125 $eventParams = $eventResult->getParameters();
126 if ($eventResult->getType() === EventResult::SUCCESS)
127 {
128 if ($eventParams)
129 {
130 if (isset($eventParams['message']) && is_array($eventParams['message']))
131 {
132 unset(
133 $eventParams['message']['MESSAGE_ID'],
134 $eventParams['message']['CHAT_ID'],
135 $eventParams['message']['AUTHOR_ID'],
136 $eventParams['message']['FROM_USER_ID']
137 );
138 $message->fill($eventParams['message']);
139 }
140 if (isset($eventParams['parameters']) && is_array($eventParams['parameters']))
141 {
142 $this->sendingConfig->fill($eventParams['parameters']);
143 }
144 }
145 }
146 elseif ($eventResult->getType() === EventResult::ERROR)
147 {
148 if ($eventParams && isset($eventParams['error']))
149 {
150 if ($eventParams['error'] instanceof Main\Error)
151 {
152 $result->addError($eventParams['error']);
153 }
154 elseif (is_string($eventParams['error']))
155 {
156 $result->addError(new ChatError(ChatError::BEFORE_SEND_EVENT, $eventParams['error']));
157 }
158 }
159 else
160 {
161 $result->addError(new ChatError(ChatError::BEFORE_SEND_EVENT));
162 }
163 }
164 }
165
166 return $result;
167 }
168
177 public function fireEventBeforeMessageSend(Chat $chat, Message $message): Result
178 {
179 $result = new Result;
180
181 $compatibleFields = array_merge(
182 $message->toArray(),
183 $this->sendingConfig->toArray(),
184 );
185 $compatibleChatFields = $chat->toArray();
186
187 foreach (\GetModuleEvents('im', self::EVENT_BEFORE_CHAT_MESSAGE_ADD, true) as $event)
188 {
189 $eventResult = \ExecuteModuleEventEx($event, [$compatibleFields, $compatibleChatFields]);
190 if ($eventResult === false || isset($eventResult['result']) && $eventResult['result'] === false)
191 {
192 $reason = $this->detectReasonSendError($chat->getType(), $eventResult['reason'] ?? '');
193 return $result->addError(new ChatError(ChatError::FROM_OTHER_MODULE, $reason));
194 }
195
196 if (isset($eventResult['fields']) && is_array($eventResult['fields']))
197 {
198 unset(
199 $eventResult['fields']['MESSAGE_ID'],
200 $eventResult['fields']['CHAT_ID'],
201 $eventResult['fields']['AUTHOR_ID'],
202 $eventResult['fields']['FROM_USER_ID']
203 );
204 $message->fill($eventResult['fields']);
205 $this->sendingConfig->fill($eventResult['fields']);
206 }
207 }
208
209 return $result;
210 }
211
220 public function fireEventAfterMessageSend(Chat $chat, Message $message): Result
221 {
222 $result = new Result;
223
224 $compatibleFields = array_merge(
225 $message->toArray(),
226 $chat->toArray(),
227 [
228 'FILES' => [], //todo: Move it into Message
229 'EXTRA_PARAMS' => [],
230 'URL_ATTACH' => [],
231 'BOT_IN_CHAT' => [],
232 ],
233 $this->sendingConfig->toArray(),
234 );
235
236 foreach (\GetModuleEvents('im', static::EVENT_AFTER_MESSAGE_ADD, true) as $event)
237 {
238 \ExecuteModuleEventEx($event, [$message->getMessageId(), $compatibleFields]);
239 }
240
241 return $result;
242 }
243
252 public function fireEventBeforeNotifySend(Chat $chat, Message $message): Result
253 {
254 $result = new Result;
255
256 $compatibleFields = array_merge(
257 $message->toArray(),
258 $chat->toArray(),
259 $this->sendingConfig->toArray(),
260 );
261
262 foreach (\GetModuleEvents('im', self::EVENT_BEFORE_NOTIFY_ADD, true) as $arEvent)
263 {
264 $eventResult = \ExecuteModuleEventEx($arEvent, [&$compatibleFields]);
265 if ($eventResult === false || isset($eventResult['result']) && $eventResult['result'] === false)
266 {
267 $reason = $this->detectReasonSendError($chat->getType(), $eventResult['reason'] ?? '');
268 return $result->addError(new ChatError(ChatError::FROM_OTHER_MODULE, $reason));
269 }
270 }
271
272 return $result;
273 }
274
283 public function fireEventAfterNotifySend(Chat $chat, Message $message): Result
284 {
285 $result = new Result;
286
287 $compatibleFields = array_merge(
288 $message->toArray(),
289 $chat->toArray(),
290 $this->sendingConfig->toArray(),
291 );
292
293 foreach(\GetModuleEvents('im', self::EVENT_AFTER_NOTIFY_ADD, true) as $event)
294 {
295 \ExecuteModuleEventEx($event, [(int)$message->getMessageId(), $compatibleFields]);
296 }
297
298 return $result;
299 }
300
301 private function detectReasonSendError($type, $reason = ''): string
302 {
303 if (!empty($reason))
304 {
305 $sanitizer = new \CBXSanitizer;
306 $sanitizer->addTags([
307 'a' => ['href','style', 'target'],
308 'b' => [],
309 'u' => [],
310 'i' => [],
311 'br' => [],
312 'span' => ['style'],
313 ]);
314 $reason = $sanitizer->sanitizeHtml($reason);
315 }
316 else
317 {
318 if ($type == Chat::IM_TYPE_PRIVATE)
319 {
320 $reason = Loc::getMessage('IM_ERROR_MESSAGE_CANCELED');
321 }
322 else if ($type == Chat::IM_TYPE_SYSTEM)
323 {
324 $reason = Loc::getMessage('IM_ERROR_NOTIFY_CANCELED');
325 }
326 else
327 {
328 $reason = Loc::getMessage('IM_ERROR_GROUP_CANCELED');
329 }
330 }
331
332 return $reason;
333 }
334 //endregion
335
336 public function prepareFields(
337 Chat $chat,
338 array $fieldsToSend,
339 ?MessageCollection $forwardMessages,
340 ?\CRestServer $server
341 ): Result
342 {
343 if (isset($forwardMessages))
344 {
345 if (!isset($fieldsToSend['MESSAGE']) && !isset($fieldsToSend['ATTACH']))
346 {
347 return (new Result())->setResult([]);
348 }
349 }
350
351 $result = $this->checkMessage($fieldsToSend);
352 if(!$result->isSuccess())
353 {
354 return $result;
355 }
356 $fieldsToSend = $result->getResult();
357
358 $chatData = $this->getChatData($chat, $fieldsToSend, $server);
359 $fieldsToSend = array_merge($fieldsToSend, $chatData);
360
361 if (isset($fieldsToSend['ATTACH']))
362 {
363 $result = $this->checkAttach($fieldsToSend);
364 if (!$result->isSuccess())
365 {
366 return $result;
367 }
368
369 $fieldsToSend = $result->getResult();
370 }
371
372 if (!empty($fieldsToSend['KEYBOARD']))
373 {
374 $result = $this->checkKeyboard($fieldsToSend);
375 if (!$result->isSuccess())
376 {
377 return $result;
378 }
379
380 $fieldsToSend = $result->getResult();
381 }
382
383 if (!empty($fieldsToSend['MENU']))
384 {
385 $result = $this->checkMenu($fieldsToSend);
386 if (!$result->isSuccess())
387 {
388 return $result;
389 }
390
391 $fieldsToSend = $result->getResult();
392 }
393
394 if (isset($fieldsToSend['REPLY_ID']) && (int)$fieldsToSend['REPLY_ID'] > 0)
395 {
396 $result = $this->checkReply($fieldsToSend, $chat);
397 if (!$result->isSuccess())
398 {
399 return $result;
400 }
401
402 $fieldsToSend = $result->getResult();
403 }
404
405 $result = $this->checkParams($fieldsToSend, $server);
406
407 return $result->setResult($fieldsToSend);
408 }
409
410 private function checkMessage(array $fieldsToSend): Result
411 {
412 $result = new Result();
413 if(isset($fieldsToSend['MESSAGE']))
414 {
415 if (!is_string($fieldsToSend['MESSAGE']))
416 {
417 return $result->addError(new MessageError(MessageError::EMPTY_MESSAGE,'Wrong message type'));
418 }
419
420 $fieldsToSend['MESSAGE'] = trim($fieldsToSend['MESSAGE']);
421
422 if ($fieldsToSend['MESSAGE'] === '' && empty($arguments['ATTACH']))
423 {
424 return $result->addError(new MessageError(
426 "Message can't be empty"
427 ));
428 }
429 }
430 elseif (!isset($fieldsToSend['ATTACH']))
431 {
432 return $result->addError(new MessageError(MessageError::EMPTY_MESSAGE,"Message can't be empty"));
433 }
434
435 return $result->setResult($fieldsToSend);
436 }
437
438 private function getChatData(Chat $chat, array $fieldsToSend, ?\CRestServer $server): ?array
439 {
440 $userId = $chat->getContext()->getUserId();
441
442 if ($chat->getType() === Chat::IM_TYPE_PRIVATE)
443 {
444 return [
445 "MESSAGE_TYPE" => IM_MESSAGE_PRIVATE,
446 "FROM_USER_ID" => $userId,
447 "DIALOG_ID" => $chat->getDialogId(),
448 ];
449 }
450
451 if (isset($fieldsToSend['SYSTEM'], $server) && $fieldsToSend['SYSTEM'] === 'Y')
452 {
453 $fieldsToSend['MESSAGE'] = $this->prepareSystemMessage($server, $fieldsToSend['MESSAGE']);
454 }
455
456 return [
457 'MESSAGE' => $fieldsToSend['MESSAGE'],
458 "MESSAGE_TYPE" => IM_MESSAGE_CHAT,
459 "FROM_USER_ID" => $userId,
460 "DIALOG_ID" => $chat->getDialogId(),
461 ];
462 }
463
464 private function prepareSystemMessage(\CRestServer $server, string $message): string
465 {
466 $clientId = $server->getClientId();
467
468 if (!$clientId)
469 {
470 return $message;
471 }
472
473 $result = \Bitrix\Rest\AppTable::getList(
474 array(
475 'filter' => array(
476 '=CLIENT_ID' => $clientId
477 ),
478 'select' => array(
479 'CODE',
480 'APP_NAME',
481 'APP_NAME_DEFAULT' => 'LANG_DEFAULT.MENU_NAME',
482 )
483 )
484 );
485 $result = $result->fetch();
486 $moduleName = !empty($result['APP_NAME'])
487 ? $result['APP_NAME']
488 : (!empty($result['APP_NAME_DEFAULT'])
489 ? $result['APP_NAME_DEFAULT']
490 : $result['CODE']
491 )
492 ;
493
494 return "[b]" . $moduleName . "[/b]\n" . $message;
495 }
496
497 private function checkAttach(array $fieldsToSend): Result
498 {
499 $result = new Result();
500
501 $attach = CIMMessageParamAttach::GetAttachByJson($fieldsToSend['ATTACH']);
502 if ($attach)
503 {
504 if ($attach->IsAllowSize())
505 {
506 $fieldsToSend['ATTACH'] = $attach;
507
508 return $result->setResult($fieldsToSend);
509 }
510
511 return $result->addError(new ParamError(
513 'You have exceeded the maximum allowable size of attach'
514 ));
515 }
516
517 return $result->addError(new ParamError(ParamError::ATTACH_ERROR, 'Incorrect attach params'));
518 }
519
520 private function checkKeyboard(array $fieldsToSend): Result
521 {
522 $result = new Result();
523
524 $keyboard = [];
525 $keyboardField = $fieldsToSend['KEYBOARD'];
526
527 if (is_string($keyboardField))
528 {
529 $keyboardField = \CUtil::JsObjectToPhp($keyboardField);
530 }
531 if (!isset($keyboardField['BUTTONS']))
532 {
533 $keyboard['BUTTONS'] = $keyboardField;
534 }
535 else
536 {
537 $keyboard = $keyboardField;
538 }
539
540 $keyboard['BOT_ID'] = $fieldsToSend['BOT_ID'];
541 $keyboard = \Bitrix\Im\Bot\Keyboard::getKeyboardByJson($keyboard);
542
543 if ($keyboard)
544 {
545 $fieldsToSend['KEYBOARD'] = $keyboard;
546
547 return $result->setResult($fieldsToSend);
548 }
549
550 return $result->addError(new ParamError(ParamError::KEYBOARD_ERROR,'Incorrect keyboard params'));
551 }
552
553 private function checkMenu(array $fieldsToSend): Result
554 {
555 $result = new Result();
556
557 $menu = [];
558 $menuField = $fieldsToSend['MENU'];
559
560 if (is_string($menuField))
561 {
562 $menuField = \CUtil::JsObjectToPhp($menuField);
563 }
564
565 if (!isset($menuField['ITEMS']))
566 {
567 $menu['ITEMS'] = $menuField;
568 }
569 else
570 {
571 $menu = $menuField;
572 }
573
574 $menu['BOT_ID'] = $fieldsToSend['BOT_ID'];
575 $menu = \Bitrix\Im\Bot\ContextMenu::getByJson($menu);
576
577 if ($menu)
578 {
579 $fieldsToSend['MENU'] = $menu;
580
581 return $result->setResult($fieldsToSend);
582 }
583
584 return $result->addError(new ParamError(ParamError::MENU_ERROR, 'Incorrect menu params'));
585 }
586
587 private function checkReply(array $fieldsToSend, Chat $chat): Result
588 {
589 $result = new Result();
590
591 $message = new \Bitrix\Im\V2\Message((int)$fieldsToSend['REPLY_ID']);
592 if (!$message->hasAccess())
593 {
594 return $result->addError(new MessageError(MessageError::REPLY_ERROR, 'Action unavailable'));
595 }
596
597 if ($message->getChat()->getId() !== $chat->getId())
598 {
599 return $result->addError(new MessageError(
601 'You can only reply to a message within the same chat')
602 );
603 }
604
605 $fieldsToSend['PARAMS']['REPLY_ID'] = $message->getId();
606
607 return $result->setResult($fieldsToSend);
608 }
609
610 private function checkParams(array $fieldsToSend, ?\CRestServer $server): Result
611 {
612 $result = new Result();
613 $checkAuth = isset($server) ? $server->getAuthType() !== \Bitrix\Rest\SessionAuth\Auth::AUTH_TYPE : true;
614
615 if (
616 isset($fieldsToSend['SYSTEM']) && $fieldsToSend['SYSTEM'] === 'Y'
617 && $checkAuth
618 && \Bitrix\Im\Dialog::hasAccess($fieldsToSend['DIALOG_ID'])
619 )
620 {
621 $fieldsToSend['SYSTEM'] = 'Y';
622 }
623
624 if (isset($fieldsToSend['URL_PREVIEW']) && $fieldsToSend['URL_PREVIEW'] === 'N')
625 {
626 $fieldsToSend['URL_PREVIEW'] = 'N';
627 }
628
629 if (isset($fieldsToSend['SKIP_CONNECTOR']) && mb_strtoupper($fieldsToSend['SKIP_CONNECTOR']) === 'Y')
630 {
631 $fieldsToSend['SKIP_CONNECTOR'] = 'Y';
632 $fieldsToSend['SILENT_CONNECTOR'] = 'Y';
633 }
634
635 if (!empty($fieldsToSend['TEMPLATE_ID']))
636 {
637 $fieldsToSend['TEMPLATE_ID'] = mb_substr((string)$fieldsToSend['TEMPLATE_ID'], 0, 255);
638 }
639
640 return $result->setResult($fieldsToSend);
641 }
642}
static getType($chatData)
Definition chat.php:41
static hasAccess($dialogId, $userId=null)
Definition dialog.php:143
static validate(string $uuid)
Definition Uuid.php:87
const IM_TYPE_PRIVATE
Definition Chat.php:55
const IM_TYPE_SYSTEM
Definition Chat.php:59
prepareFields(Chat $chat, array $fieldsToSend, ?MessageCollection $forwardMessages, ?\CRestServer $server)
fireEventAfterMessageSend(Chat $chat, Message $message)
fireEventBeforeSend(Chat $chat, Message $message)
__construct(?SendingConfig $sendingConfig=null)
fireEventAfterNotifySend(Chat $chat, Message $message)
fireEventBeforeNotifySend(Chat $chat, Message $message)
fireEventBeforeMessageSend(Chat $chat, Message $message)
setResult($result)
Definition Result.php:17
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29