1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
SendingService.php
См. документацию.
1<?php
2
3namespace Bitrix\Im\V2\Message\Send;
4
5use Bitrix\Im\V2\Bot\BotService;
6use Bitrix\Im\V2\Entity\User\User;
7use Bitrix\Im\V2\Message\Param\ParamError;
8use Bitrix\Im\V2\MessageCollection;
9use Bitrix\Main\Localization\Loc;
10use Bitrix\Im\Message\Uuid;
11use Bitrix\Im\V2\Message;
12use Bitrix\Im\V2\Result;
13use Bitrix\Im\V2\Chat;
14use Bitrix\Im\V2\Chat\ChatError;
15use Bitrix\Im\V2\Message\MessageError;
16use Bitrix\Im\V2\Common\ContextCustomer;
17use CIMMessageParamAttach;
18
20{
21 use ContextCustomer;
22
23 private SendingConfig $sendingConfig;
24
25 protected ?Uuid $uuidService;
26
27 public const
28 EVENT_BEFORE_MESSAGE_ADD = 'OnBeforeMessageAdd',//new
29 EVENT_AFTER_MESSAGE_ADD = 'OnAfterMessagesAdd',
30 EVENT_BEFORE_CHAT_MESSAGE_ADD = 'OnBeforeChatMessageAdd',
31 EVENT_BEFORE_NOTIFY_ADD = 'OnBeforeMessageNotifyAdd',
32 EVENT_AFTER_NOTIFY_ADD = 'OnAfterMessageNotifyAdd'
33 ;
34
38 public function __construct(?SendingConfig $sendingConfig = null)
39 {
40 if ($sendingConfig === null)
41 {
42 $sendingConfig = new SendingConfig();
43 }
44 $this->sendingConfig = $sendingConfig;
45 }
46
47 public function getConfig(): SendingConfig
48 {
49 return $this->sendingConfig;
50 }
51
52 //region UUID
53
59 {
60 $result = new Result;
61
62 if (!$this->needToCheckDuplicate($message))
63 {
64 return $result;
65 }
66
67 $this->uuidService = new Uuid($message->getUuid());
68 $alreadyExists = !$this->uuidService->add();
69 if (!$alreadyExists)
70 {
71 return $result;
72 }
73
74 $messageIdByUuid = $this->uuidService->getMessageId();
75 // if we got message_id, then message already exists, and we don't need to add it, so return with ID.
76 if (!is_null($messageIdByUuid))
77 {
78 return $result->setResult(['messageId' => $messageIdByUuid]);
79 }
80
81 // if there is no message_id and entry date is expired,
82 // then update date_create and return false to delay next sending on the client.
83 if (!$this->uuidService->updateIfExpired())
84 {
86 }
87
88 return $result;
89 }
90
91 protected function needToCheckDuplicate(Message $message): bool
92 {
93 return $message->getUuid() && !$message->isSystem() && Uuid::validate($message->getUuid());
94 }
95
100 public function updateMessageUuid(Message $message): void
101 {
102 if (isset($this->uuidService))
103 {
104 $this->uuidService->updateMessageId($message->getMessageId());
105 }
106 }
107
108 //endregion
109
110 //region Events
111
121 {
122 $result = new Result;
123
124 $compatibleFields = $this->getMessageForEvent($message);
125 $compatibleChatFields = $this->getChatForEvent($message);
126
127 foreach (\GetModuleEvents('im', self::EVENT_BEFORE_CHAT_MESSAGE_ADD, true) as $event)
128 {
129 $eventResult = \ExecuteModuleEventEx($event, [$compatibleFields, $compatibleChatFields]);
130 if ($eventResult === false || (isset($eventResult['result']) && $eventResult['result'] === false))
131 {
132 $reason = $this->detectReasonSendError($chat->getType(), $eventResult['reason'] ?? '');
133 return $result->addError(new ChatError(ChatError::FROM_OTHER_MODULE, $reason));
134 }
135
136 if (isset($eventResult['fields']) && is_array($eventResult['fields']))
137 {
138 unset(
139 $eventResult['fields']['MESSAGE_ID'],
140 $eventResult['fields']['CHAT_ID'],
141 $eventResult['fields']['AUTHOR_ID'],
142 $eventResult['fields']['FROM_USER_ID']
143 );
144 $message->fill($eventResult['fields']);
145 $this->sendingConfig->fill($eventResult['fields']);
146 }
147 }
148
149 return $result;
150 }
151
161 {
162 $result = new Result;
163
164 $compatibleFields = array_merge(
165 $this->getMessageForEvent($message, false),
166 $this->getChatForEvent($message, true),
167 $this->sendingConfig->toArray(),
168 );
169
170 foreach (\GetModuleEvents('im', static::EVENT_AFTER_MESSAGE_ADD, true) as $event)
171 {
172 \ExecuteModuleEventEx($event, [$message->getMessageId(), $compatibleFields]);
173 }
174
175 (new BotService($this->sendingConfig))->setContext($this->context)->runMessageCommand($message->getId(), $compatibleFields);
176
177 return $result;
178 }
179
181 {
182 if (!$this->sendingConfig->isSkipFireEventBeforeMessageNotifySend())
183 {
185
186 if (!$result->isSuccess())
187 {
188 return $result;
189 }
190 }
191
192 if (!$chat instanceof Chat\PrivateChat)
193 {
194 return $this->fireEventBeforeMessageSend($chat, $message);
195 }
196
197 return new Result();
198 }
199
209 {
210 $result = new Result;
211
212 $compatibleFields = array_merge(
213 $this->getMessageForEvent($message),
214 $this->sendingConfig->toArray(),
215 );
216 $compatibleFieldsCopy = $compatibleFields;
217
218 foreach (\GetModuleEvents('im', self::EVENT_BEFORE_NOTIFY_ADD, true) as $arEvent)
219 {
220 $eventResult = \ExecuteModuleEventEx($arEvent, [&$compatibleFields]);
221 if ($eventResult === false || (isset($eventResult['result']) && $eventResult['result'] === false))
222 {
223 $reason = $this->detectReasonSendError($chat->getType(), $eventResult['reason'] ?? '');
224 return $result->addError(new ChatError(ChatError::FROM_OTHER_MODULE, $reason));
225 }
226 if ($compatibleFields !== $compatibleFieldsCopy)
227 {
228 $message->fill($compatibleFields);
229 $this->sendingConfig->fillByLegacy($compatibleFields);
230 }
231 }
232
233 return $result;
234 }
235
245 {
246 $result = new Result;
247
248 $compatibleFields = array_merge(
249 $message->toArray(),
250 $chat->toArray(),
251 $this->sendingConfig->toArray(),
252 );
253
254 foreach(\GetModuleEvents('im', self::EVENT_AFTER_NOTIFY_ADD, true) as $event)
255 {
256 \ExecuteModuleEventEx($event, [(int)$message->getMessageId(), $compatibleFields]);
257 }
258
259 return $result;
260 }
261
262 protected function getMessageForEvent(Message $message, bool $onlyIdFiles = true): array //todo private
263 {
264 $res = [
265 'MESSAGE' => $message->getMessage(),
266 'TEMPLATE_ID' => $message->getUuid(),
267 'MESSAGE_TYPE' => $message->getChat()->getType(),
268 'FROM_USER_ID' => $message->getAuthorId(),
269 'DIALOG_ID' => $message->getChat()->getDialogId(),
270 'TO_CHAT_ID' => $message->getChatId(),
271 'MESSAGE_OUT' => $message->getMessageOut() ?? '',
272 'PARAMS' => $message->getEnrichedParams()->toArray(),
273 'EXTRA_PARAMS' => $message->getPushParams() ?? [],
274 'NOTIFY_MODULE' => $message->getNotifyModule(),
275 'NOTIFY_EVENT' => $message->getNotifyEvent(),
276 'URL_ATTACH' => $message->getUrl()?->getUrlAttach()?->GetArray() ?? [],
277 'AUTHOR_ID' => $message->getAuthorId(),
278 'SYSTEM' => $message->isSystem() ? 'Y' : 'N',
279 ];
280
281 $res['FILES'] = [];
282
283 if ($onlyIdFiles)
284 {
285 $res['FILES'] = $message->getFiles()->getIds();
286 }
287 else
288 {
289 foreach ($message->getFiles() as $file)
290 {
291 $res['FILES'][$file->getId()] = $file->toRestFormat();
292 }
293 }
294
295 if ($message->getChat() instanceof Chat\PrivateChat)
296 {
297 $res['TO_USER_ID'] = $message->getChat()->getDialogId();
298 }
299
300 $res = array_merge($res, $this->sendingConfig->toArray());
301
302 return $res;
303 }
304
305 protected function getChatForEvent(Message $message, bool $withBot = false): array
306 {
307 $chat = $message->getChat();
308 if ($chat instanceof Chat\PrivateChat)
309 {
310 return [];
311 }
312 $authorRelation = $chat->getRelationByUserId($message->getAuthorId());
313
314 $res = [
315 'CHAT_ID' => $chat->getId(),
316 'CHAT_PARENT_ID' => $chat->getParentChatId() ?? 0,
317 'CHAT_PARENT_MID' => $chat->getParentMessageId() ?? 0,
318 'CHAT_TITLE' => $chat->getTitle() ?? '',
319 'CHAT_AUTHOR_ID' => $chat->getAuthorId(),
320 'CHAT_TYPE' => $chat->getType(),
321 'CHAT_AVATAR' => $chat->getAvatarId(),
322 'CHAT_COLOR' => $chat->getColor(),
323 'CHAT_ENTITY_TYPE' => $chat->getEntityType(),
324 'CHAT_ENTITY_ID' => $chat->getEntityId(),
325 'CHAT_ENTITY_DATA_1' => $chat->getEntityData1(),
326 'CHAT_ENTITY_DATA_2' => $chat->getEntityData2(),
327 'CHAT_ENTITY_DATA_3' => $chat->getEntityData3(),
328 'CHAT_EXTRANET' => ($chat->getExtranet() ?? false) ? 'Y' : 'N',
329 'CHAT_PREV_MESSAGE_ID' => $chat->getPrevMessageId() ?? 0,
330 'CHAT_CAN_POST' => $chat->getManageMessages(),
331 'RID' => $authorRelation?->getUserId() ?? 1,
332 'IS_MANAGER' => ($authorRelation?->getManager() ?? false) ? 'Y' : 'N',
333 ];
334
335 if ($withBot)
336 {
337 $res['BOT_IN_CHAT'] = $chat->getBotInChat();
338 }
339
340 return $res;
341 }
342
343 private function detectReasonSendError($type, $reason = ''): string
344 {
345 if (!empty($reason))
346 {
347 $sanitizer = new \CBXSanitizer;
348 $sanitizer->addTags([
349 'a' => ['href','style', 'target'],
350 'b' => [],
351 'u' => [],
352 'i' => [],
353 'br' => [],
354 'span' => ['style'],
355 ]);
356 $reason = $sanitizer->sanitizeHtml($reason);
357 }
358 else
359 {
360 if ($type == Chat::IM_TYPE_PRIVATE)
361 {
362 $reason = Loc::getMessage('IM_ERROR_MESSAGE_CANCELED');
363 }
364 else if ($type == Chat::IM_TYPE_SYSTEM)
365 {
366 $reason = Loc::getMessage('IM_ERROR_NOTIFY_CANCELED');
367 }
368 else
369 {
370 $reason = Loc::getMessage('IM_ERROR_GROUP_CANCELED');
371 }
372 }
373
374 return $reason;
375 }
376 //endregion
377
378 public function prepareFields(
379 Chat $chat,
380 array $fieldsToSend,
381 ?MessageCollection $forwardMessages,
382 ?\CRestServer $server
383 ): Result
384 {
385 if (isset($forwardMessages))
386 {
387 if (!isset($fieldsToSend['MESSAGE']) && !isset($fieldsToSend['ATTACH']))
388 {
389 return (new Result())->setResult([]);
390 }
391 }
392
393 $result = $this->checkMessage($fieldsToSend);
394 if(!$result->isSuccess())
395 {
396 return $result;
397 }
398 $fieldsToSend = $result->getResult();
399
400 $chatData = $this->getChatData($chat, $fieldsToSend, $server);
401 $fieldsToSend = array_merge($fieldsToSend, $chatData);
402
403 if (isset($fieldsToSend['ATTACH']))
404 {
405 $result = $this->checkAttach($fieldsToSend);
406 if (!$result->isSuccess())
407 {
408 return $result;
409 }
410
411 $fieldsToSend = $result->getResult();
412 }
413
414 if (!empty($fieldsToSend['KEYBOARD']))
415 {
416 $result = $this->checkKeyboard($fieldsToSend);
417 if (!$result->isSuccess())
418 {
419 return $result;
420 }
421
422 $fieldsToSend = $result->getResult();
423 }
424
425 if (!empty($fieldsToSend['MENU']))
426 {
427 $result = $this->checkMenu($fieldsToSend);
428 if (!$result->isSuccess())
429 {
430 return $result;
431 }
432
433 $fieldsToSend = $result->getResult();
434 }
435
436 if (isset($fieldsToSend['REPLY_ID']) && (int)$fieldsToSend['REPLY_ID'] > 0)
437 {
438 $result = $this->checkReply($fieldsToSend, $chat);
439 if (!$result->isSuccess())
440 {
441 return $result;
442 }
443
444 $fieldsToSend = $result->getResult();
445 }
446
447 $fieldsToSend = $this->checkParams($fieldsToSend, $server);
448 $fieldsToSend = isset($fieldsToSend['COPILOT']) ?$this->checkCopilotParams($fieldsToSend) : $fieldsToSend;
449
450 if (isset($fieldsToSend['COPILOT']))
451 {
452 $fieldsToSend['PARAMS'][Message\Params::COPILOT_PROMPT_CODE] = $fieldsToSend['COPILOT'][Message\Params::COPILOT_PROMPT_CODE];
453 }
454
455 return $result->setResult($fieldsToSend);
456 }
457
458 private function checkCopilotParams(array $fieldsToSend): array
459 {
460 $copilotData = [];
461
462 if (isset($fieldsToSend['COPILOT']) && is_array($fieldsToSend['COPILOT']))
463 {
464 foreach ($fieldsToSend['COPILOT'] as $key => $item)
465 {
466 if ($key === 'promptCode' && is_string($item))
467 {
468 $copilotData[Message\Params::COPILOT_PROMPT_CODE] = $item;
469 }
470 }
471 }
472
473 $fieldsToSend['COPILOT'] = $copilotData;
474
475 return $fieldsToSend;
476 }
477
478 private function checkMessage(array $fieldsToSend): Result
479 {
480 $result = new Result();
481 if(isset($fieldsToSend['MESSAGE']))
482 {
483 if (!is_string($fieldsToSend['MESSAGE']))
484 {
485 return $result->addError(new MessageError(MessageError::EMPTY_MESSAGE,'Wrong message type'));
486 }
487
488 $fieldsToSend['MESSAGE'] = trim($fieldsToSend['MESSAGE']);
489
490 if ($fieldsToSend['MESSAGE'] === '' && empty($arguments['ATTACH']))
491 {
492 return $result->addError(new MessageError(
494 "Message can't be empty"
495 ));
496 }
497 }
498 elseif (!isset($fieldsToSend['ATTACH']))
499 {
500 return $result->addError(new MessageError(MessageError::EMPTY_MESSAGE,"Message can't be empty"));
501 }
502
503 return $result->setResult($fieldsToSend);
504 }
505
506 private function getChatData(Chat $chat, array $fieldsToSend, ?\CRestServer $server): ?array
507 {
508 $userId = $chat->getContext()->getUserId();
509
510 if ($chat->getType() === Chat::IM_TYPE_PRIVATE)
511 {
512 return [
513 "MESSAGE_TYPE" => IM_MESSAGE_PRIVATE,
514 "FROM_USER_ID" => $userId,
515 "DIALOG_ID" => $chat->getDialogId(),
516 ];
517 }
518
519 if (isset($fieldsToSend['SYSTEM'], $server) && $fieldsToSend['SYSTEM'] === 'Y')
520 {
521 $fieldsToSend['MESSAGE'] = $this->prepareSystemMessage($server, $fieldsToSend['MESSAGE']);
522 }
523
524 return [
525 'MESSAGE' => $fieldsToSend['MESSAGE'],
526 "MESSAGE_TYPE" => IM_MESSAGE_CHAT,
527 "FROM_USER_ID" => $userId,
528 "DIALOG_ID" => $chat->getDialogId(),
529 ];
530 }
531
532 private function prepareSystemMessage(\CRestServer $server, string $message): string
533 {
534 $clientId = $server->getClientId();
535
536 if (!$clientId)
537 {
538 return $message;
539 }
540
541 $result = \Bitrix\Rest\AppTable::getList(
542 array(
543 'filter' => array(
544 '=CLIENT_ID' => $clientId
545 ),
546 'select' => array(
547 'CODE',
548 'APP_NAME',
549 'APP_NAME_DEFAULT' => 'LANG_DEFAULT.MENU_NAME',
550 )
551 )
552 );
553 $result = $result->fetch();
554 $moduleName = !empty($result['APP_NAME'])
555 ? $result['APP_NAME']
556 : (!empty($result['APP_NAME_DEFAULT'])
557 ? $result['APP_NAME_DEFAULT']
558 : $result['CODE']
559 )
560 ;
561
562 return "[b]" . $moduleName . "[/b]\n" . $message;
563 }
564
565 private function checkAttach(array $fieldsToSend): Result
566 {
567 $result = new Result();
568
569 $attach = CIMMessageParamAttach::GetAttachByJson($fieldsToSend['ATTACH']);
570 if ($attach)
571 {
572 if ($attach->IsAllowSize())
573 {
574 $fieldsToSend['ATTACH'] = $attach;
575
576 return $result->setResult($fieldsToSend);
577 }
578
579 return $result->addError(new ParamError(
581 'You have exceeded the maximum allowable size of attach'
582 ));
583 }
584
585 return $result->addError(new ParamError(ParamError::ATTACH_ERROR, 'Incorrect attach params'));
586 }
587
588 private function checkKeyboard(array $fieldsToSend): Result
589 {
590 $result = new Result();
591
592 $keyboard = [];
593 $keyboardField = $fieldsToSend['KEYBOARD'];
594
595 if (is_string($keyboardField))
596 {
597 $keyboardField = \CUtil::JsObjectToPhp($keyboardField);
598 }
599 if (!isset($keyboardField['BUTTONS']))
600 {
601 $keyboard['BUTTONS'] = $keyboardField;
602 }
603 else
604 {
605 $keyboard = $keyboardField;
606 }
607
608 $keyboard['BOT_ID'] = $fieldsToSend['BOT_ID'];
609 $keyboard = \Bitrix\Im\Bot\Keyboard::getKeyboardByJson($keyboard);
610
611 if ($keyboard)
612 {
613 $fieldsToSend['KEYBOARD'] = $keyboard;
614
615 return $result->setResult($fieldsToSend);
616 }
617
618 return $result->addError(new ParamError(ParamError::KEYBOARD_ERROR,'Incorrect keyboard params'));
619 }
620
621 private function checkMenu(array $fieldsToSend): Result
622 {
623 $result = new Result();
624
625 $menu = [];
626 $menuField = $fieldsToSend['MENU'];
627
628 if (is_string($menuField))
629 {
630 $menuField = \CUtil::JsObjectToPhp($menuField);
631 }
632
633 if (!isset($menuField['ITEMS']))
634 {
635 $menu['ITEMS'] = $menuField;
636 }
637 else
638 {
639 $menu = $menuField;
640 }
641
642 $menu['BOT_ID'] = $fieldsToSend['BOT_ID'];
643 $menu = \Bitrix\Im\Bot\ContextMenu::getByJson($menu);
644
645 if ($menu)
646 {
647 $fieldsToSend['MENU'] = $menu;
648
649 return $result->setResult($fieldsToSend);
650 }
651
652 return $result->addError(new ParamError(ParamError::MENU_ERROR, 'Incorrect menu params'));
653 }
654
655 private function checkReply(array $fieldsToSend, Chat $chat): Result
656 {
657 $result = new Result();
658
659 $message = new \Bitrix\Im\V2\Message((int)$fieldsToSend['REPLY_ID']);
660 $messageAccess = $message->checkAccess();
661 if (!$messageAccess->isSuccess())
662 {
663 return $result->addErrors($messageAccess->getErrors());
664 }
665
666 if ($message->getChat()->getId() !== $chat->getId())
667 {
668 return $result->addError(new MessageError(
670 'You can only reply to a message within the same chat')
671 );
672 }
673
674 $fieldsToSend['PARAMS']['REPLY_ID'] = $message->getId();
675
676 return $result->setResult($fieldsToSend);
677 }
678
679 private function checkParams(array $fieldsToSend, ?\CRestServer $server): array
680 {
681 $checkAuth = isset($server) ? $server->getAuthType() !== \Bitrix\Rest\SessionAuth\Auth::AUTH_TYPE : true;
682
683 if (
684 isset($fieldsToSend['SYSTEM']) && $fieldsToSend['SYSTEM'] === 'Y'
685 && (!$checkAuth || User::getCurrent()->isExtranet())
686 )
687 {
688 $fieldsToSend['SYSTEM'] = 'N';
689 }
690
691 if (isset($fieldsToSend['URL_PREVIEW']) && $fieldsToSend['URL_PREVIEW'] === 'N')
692 {
693 $fieldsToSend['URL_PREVIEW'] = 'N';
694 }
695
696 if (isset($fieldsToSend['SKIP_CONNECTOR']) && mb_strtoupper($fieldsToSend['SKIP_CONNECTOR']) === 'Y')
697 {
698 $fieldsToSend['SKIP_CONNECTOR'] = 'Y';
699 $fieldsToSend['SILENT_CONNECTOR'] = 'Y';
700 }
701
702 if (!empty($fieldsToSend['TEMPLATE_ID']))
703 {
704 $fieldsToSend['TEMPLATE_ID'] = mb_substr((string)$fieldsToSend['TEMPLATE_ID'], 0, 255);
705 }
706
707 return $fieldsToSend;
708 }
709}
$type
Определения options.php:106
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static getType($chatData, bool $camelCase=true)
Определения chat.php:45
Определения Uuid.php:12
static validate(string $uuid)
Определения Uuid.php:87
const FROM_OTHER_MODULE
Определения ChatError.php:22
const MESSAGE_DUPLICATED_BY_UUID
Определения MessageError.php:19
const COPILOT_PROMPT_CODE
Определения Params.php:70
prepareFields(Chat $chat, array $fieldsToSend, ?MessageCollection $forwardMessages, ?\CRestServer $server)
Определения SendingService.php:378
needToCheckDuplicate(Message $message)
Определения SendingService.php:91
getChatForEvent(Message $message, bool $withBot=false)
Определения SendingService.php:305
checkDuplicateByUuid(Message $message)
Определения SendingService.php:58
fireEventAfterMessageSend(Chat $chat, Message $message)
Определения SendingService.php:160
fireEventBeforeSend(Chat $chat, Message $message)
Определения SendingService.php:180
updateMessageUuid(Message $message)
Определения SendingService.php:100
__construct(?SendingConfig $sendingConfig=null)
Определения SendingService.php:38
fireEventBeforeMessageNotifySend(Chat $chat, Message $message)
Определения SendingService.php:208
fireEventAfterNotifySend(Chat $chat, Message $message)
Определения SendingService.php:244
getMessageForEvent(Message $message, bool $onlyIdFiles=true)
Определения SendingService.php:262
fireEventBeforeMessageSend(Chat $chat, Message $message)
Определения SendingService.php:120
Определения result.php:20
static GetAttachByJson($array)
Определения im_message_param.php:1184
Определения rest.php:24
getAuthType()
Определения rest.php:347
getClientId()
Определения rest.php:362
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
const IM_MESSAGE_CHAT
Определения include.php:23
const IM_MESSAGE_PRIVATE
Определения include.php:22
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
Определения Uuid.php:3
$message
Определения payment.php:8
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$clientId
Определения seo_client.php:18