38 private array $messagesForEvent = [];
39 private array $messagesDeletionModes = [];
40 private bool $byEvent =
false;
42 private bool $isModeSpecified =
false;
43 private bool $needUpdateRecent =
false;
44 private array $counters;
45 private array $lastMessageViewers;
46 private bool $isPermissionFilled =
false;
47 private \WeakMap $deletionTypeMap;
48 private \WeakMap $sortedMessagesByMode;
58 $this->deletionTypeMap = new \WeakMap();
59 foreach (MessageType::cases() as
$type)
61 $this->deletionTypeMap[
$type] = DeletionMode::None;
64 $this->sortedMessagesByMode = new \WeakMap();
65 foreach (DeletionMode::cases() as $case)
67 $this->sortedMessagesByMode[$case] = [];
78 return (
new self($collection));
83 $this->messages = $messages;
84 $chatId = $this->messages->getCommonChatId() ?? 0;
97 if (!isset($mode) || ($mode === DeletionMode::None))
99 $this->isModeSpecified =
false;
103 $this->isModeSpecified =
true;
105 ->setDeletionMode(MessageType::OwnMessageUnread, $mode)
116 return DeletionMode::None;
119 if (!$this->isModeSpecified && !$this->isPermissionFilled)
121 $this->fillPermissions();
130 $this->byEvent = $byEvent;
145 if (!$this->isModeSpecified)
147 if (!$this->chat->checkAccess($this->getContext()->getUserId())->isSuccess())
152 $this->fillPermissions();
155 $chatLastMessageId = $this->chat->getLastMessageId();
156 $this->messages->fillFiles();
158 $this->fillMessagesForEvent();
160 foreach (DeletionMode::cases() as $deletionMode)
166 ->setContext($this->getContext())
171 $this->needUpdateRecent = $this->chat->getLastMessageId() !== $chatLastMessageId;
172 $this->onAfterDelete();
177 private function onAfterDelete(): void
179 $ids = $this->sortedMessagesByMode[DeletionMode::None] ?? [];
180 $this->clearMessagesByIds($ids);
182 $this->fireEventAfterMessagesDelete();
184 $this->sendPullMessage();
186 if (Option::get(
'im',
'message_history_index'))
188 MessageIndexTable::deleteByFilter([
'=MESSAGE_ID' => $this->messages->getPrimaryIds()]);
191 (
new UrlService())->deleteUrlsByMessages($this->messages);
193 $this->deleteAnchors();
195 $this->sendAnalyticsData();
198 private function clearMessagesByIds(
array $ids): void
200 $this->messages->unsetByKeys($ids);
201 foreach ($ids as $id)
203 if (isset($this->messagesForEvent[$id]))
205 unset($this->messagesForEvent[$id]);
210 private function isCompleteDelete(
int $messageId): bool
215 $mode = $this->messagesDeletionModes[
$messageId];
228 $this->deletionTypeMap[$messageType] = $mode;
239 return $this->deletionTypeMap[$messageType] ?? DeletionMode::None;
244 foreach ($this->messages as
$message)
253 $this->sortedMessagesByMode[$mode][] =
$message->getId();
254 $this->messagesDeletionModes[
$message->getId()] = $mode;
264 $ids = $this->sortedMessagesByMode[$mode] ?? [];
265 $collection = $this->messages->filter(
276 return $message->isViewedByOthers() ? MessageType::OwnMessageRead : MessageType::OwnMessageUnread;
282 private function fillPermissions(): void
284 if ($this->isPermissionFilled)
288 $this->isPermissionFilled =
true;
291 if ($this->getContext()->getUser()->isSuperAdmin())
302 if ($this->chat instanceof
Chat\
OpenLineChat && Loader::includeModule(
'imopenlines'))
310 if ($this->chat->canDo(Action::DeleteOthersMessage))
312 $deletionModeOtherMessage = $this->chat instanceof Chat\CommentChat ? DeletionMode::Soft :
DeletionMode::Complete;
317 if ($this->chat instanceof Chat\ChannelChat || $this->chat instanceof Chat\GeneralChat)
319 if ($this->chat->canDo(Action::Send))
331 $deletionModeSelfMessage = $this->chat instanceof Chat\CommentChat ? DeletionMode::Soft :
DeletionMode::Complete;
332 $this->
setDeletionMode(MessageType::OwnMessageRead, DeletionMode::Soft)
333 ->setDeletionMode(MessageType::OwnMessageUnread, $deletionModeSelfMessage)
344 if ($this->getContext()->getUser()->isBot())
354 if ($this->chat->canDeleteOwnMessage())
356 $this->
setDeletionMode(MessageType::OwnMessageUnread, DeletionMode::Soft)
357 ->setDeletionMode(MessageType::OwnMessageRead, DeletionMode::Soft)
362 $this->chat->canDeleteMessage()
363 && $this->chat->canDo(Action::DeleteOthersMessage)
373 $this->getContext()->getUserId() ===
$message->getAuthorId()
380 $date =
$message->getDateCreate()?->toString();
382 return Loc::getMessage(
'IM_MESSAGE_DELETED_OUT', [
'#DATE#' => $date]) ??
'';
392 $relations = $this->chat->getRelations()->getUserIds();
394 foreach ($this->messages as
$message)
396 $pullMessage = $this->getFormatOpenLinePullMessage(
$message);
399 $pullMessage[
'extra'][
'is_shared_event'] =
true;
400 if ($this->chat->needToSendPublicPull())
402 \CPullWatch::AddToStack(
'IM_PUBLIC_' . $this->chat->getChatId(), $pullMessage);
409 private function sendPullMessage():
Result
420 $userId = $this->chat->getAuthorId();
421 $companionUserId = $this->chat->getCompanion(
$userId)->getId();
422 $this->sendPullMessagePrivate(
$userId, $companionUserId, $pullMessage);
423 $this->sendPullMessagePrivate($companionUserId,
$userId, $pullMessage);
427 $groupedPullMessage = $this->groupPullByCounter($pullMessage);
428 foreach ($groupedPullMessage as $pullForGroup)
430 Event::add($pullForGroup[
'users'], $pullForGroup[
'event']);
433 $pullMessage[
'extra'][
'is_shared_event'] =
true;
434 $pullMessage[
'params'][
'recentConfig'][
'sections'] = $this->chat->getRecentSectionsForGuest();
436 if ($this->chat->getType() === Chat::IM_TYPE_COMMENT)
438 \CPullWatch::AddToStack(
'IM_PUBLIC_COMMENT_' . $this->chat->getParentChatId(), $pullMessage);
441 if ($this->chat->needToSendPublicPull())
443 \CPullWatch::AddToStack(
'IM_PUBLIC_' . $this->chat->getChatId(), $pullMessage);
445 if ($this->chat->getType() === Chat::IM_TYPE_OPEN_CHANNEL && $this->needUpdateRecent)
447 Chat\OpenChannelChat::sendSharedPull($pullMessage);
454 private function sendPullMessagePrivate(
int $fromUser,
int $toUser,
array $pullMessage): void
457 $relation = $this->chat->getRelations()->getByUserId($toUser, $this->chat->getChatId());
458 if ($relation !==
null)
460 $isMuted = $relation->getNotifyBlock() ??
false;
462 $pullMessage[
'params'][
'dialogId'] = $fromUser;
463 $pullMessage[
'params'][
'fromUserId'] = $fromUser;
464 $pullMessage[
'params'][
'toUserId'] = $toUser;
465 $pullMessage[
'params'][
'counter'] = $this->getCounter($toUser);
466 $pullMessage[
'params'][
'unread'] =
Recent::isUnread($toUser, $this->chat->getType(), $fromUser);
467 $pullMessage[
'params'][
'muted'] = $isMuted;
468 if ($this->needUpdateRecent)
470 $pullMessage[
'params'][
'lastMessageViews'] = $this->getLastViewers($toUser);
472 Event::add($toUser, $pullMessage);
475 private function getFormatMessageForPull(Message
$message,
bool $completeDelete):
array
479 'text' => Loc::getMessage(
'IM_MESSAGE_DELETED'),
480 'senderId' =>
$message->getAuthorId(),
481 'params' => [
'IS_DELETED' =>
'Y',
'URL_ID' => [],
'FILE_ID' => [],
'KEYBOARD' =>
'N',
'ATTACH' => []],
482 'completelyDeleted' => $completeDelete,
486 private function getFormatOpenLinePullMessage(Message
$message):
array
491 'text' => Loc::getMessage(
'IM_MESSAGE_DELETED'),
492 'senderId' =>
$message->getAuthorId(),
493 'params' => [
'IS_DELETED' =>
'Y',
'URL_ID' => [],
'FILE_ID' => [],
'KEYBOARD' =>
'N',
'ATTACH' => []],
494 'chatId' => $this->chat->getChatId(),
495 'dialogId' => $this->chat->getDialogId(),
497 $isComplete = $this->isCompleteDelete((
int)
$message->getId());
501 'command' => $isComplete ?
'messageDeleteComplete' :
'messageDelete',
503 'push' => $isComplete ? [
'badge' =>
'Y'] : [],
512 'chatId' => $this->chat->getChatId(),
513 'type' => $this->chat->getType() === Chat::IM_TYPE_PRIVATE ?
'private' :
'chat',
517 'counterType' => $this->chat->getCounterType()->value,
518 'recentConfig' => $this->chat->getRecentConfig()->toPullFormat(),
521 foreach ($this->messages as
$message)
523 $isCompleteDelete = $this->isCompleteDelete(
$message->getId());
524 $params[
'messages'][] = $this->getFormatMessageForPull(
$message, $isCompleteDelete);
529 $params[
'dialogId'] = $this->chat->getDialogId();
532 $chatLastMessageId = $this->chat->getLastMessageId();
533 if ($this->needUpdateRecent && isset($chatLastMessageId))
535 if ($chatLastMessageId !== 0)
537 $newLastMessage =
new Message($chatLastMessageId);
538 if ($newLastMessage->getId())
540 $params[
'newLastMessage'] = $this->formatNewLastMessage($newLastMessage);
545 $params[
'newLastMessage'] = [
'id' => 0];
551 'command' =>
'messageDeleteV2',
553 'push' => [
'badge' =>
'Y'],
558 private function groupPullByCounter(
array $pullMessage):
array
562 $relations = $this->chat->getRelations();
563 $unreadList =
Recent::getUnread($this->chat->getType(), $this->chat->getDialogId());
564 $messageId = $this->messages->getAny()?->getId() ?? 0;
566 foreach ($relations as $relation)
568 $user = $relation->getUser();
570 (!
$user->isActive() &&
$user->getExternalAuthId() !== \
Bitrix\
Im\Bot::EXTERNAL_AUTH_ID)
571 || ($this->chat->getEntityType() === Chat::ENTITY_TYPE_LINE &&
$user->getExternalAuthId() ===
'imconnector')
577 $userId = $relation->getUserId();
579 $pullMessage[
'params'][
'unread'] = $unreadList[
$userId] ??
false;
580 $pullMessage[
'params'][
'muted'] = $relation->getNotifyBlock() ??
false;
582 $events[
$userId] = $pullMessage;
585 if ($this->needUpdateRecent)
587 $lastMessageViews = $this->getLastViewers(
$userId);
588 $events[
$userId][
'params'][
'lastMessageViews'] = $lastMessageViews;
589 $count = $lastMessageViews[
'countOfViewers'] ?? 0;
592 $unreadGroupFlag = $pullMessage[
'params'][
'unread'] ? 1 : 0;
593 $mutedGroupFlag = $pullMessage[
'params'][
'muted'] ? 1 : 0;
595 $events[
$userId][
'params'][
'counter'] = $this->getCounter(
$userId);
598 . $this->chat->getChatId()
600 .
'_'. $events[
$userId][
'params'][
'counter']
602 .
'_'. $unreadGroupFlag
603 .
'_'. $mutedGroupFlag
607 return Message\Send\PushService::getEventByCounterGroup($events);
610 private function fireEventAfterMessagesDelete(): Result
613 foreach ($this->messages as
$message)
616 if (!isset($id, $this->messagesForEvent[$id]))
621 $messageForEvent = $this->messagesForEvent[$id];
623 \Bitrix\Im\Bot::onMessageDelete(
630 'USER_ID' => $this->getContext()->getUserId(),
631 'COMPLETE_DELETE' => $this->isCompleteDelete($id),
632 'BY_EVENT' => $this->byEvent,
644 private function getCounter(
int $userId): int
646 $this->counters ??= (
new Message\CounterService())
647 ->getByChatForEachUsers($this->chat->getChatId(), $this->chat->getRelations()->getUserIds())
650 return $this->counters[
$userId] ?? 0;
653 private function formatNewLastMessage(Message
$message):
array
660 if (
$message->getFiles()->count() <= 0)
665 $file =
$message->getFiles()->getAny();
672 $result[
'file'] = [
'type' => $file->getContentType(),
'name' => $file->getDiskFile()?->getName()];
677 private function fillMessagesForEvent(): void
679 foreach ($this->messages as
$message)
684 $this->messagesForEvent[$id] = $this->getMessageForEvent(
$message);
694 'AUTHOR_ID' =>
$message->getAuthorId(),
695 'MESSAGE' => $this->getMessageOut(
$message),
696 'MESSAGE_OUT' =>
$message->getMessageOut(),
697 'DATE_CREATE' =>
$message->getDateCreate()?->toUserTime()->getTimestamp(),
698 'EMAIL_TEMPLATE' =>
$message->getEmailTemplate(),
699 'NOTIFY_TYPE' =>
$message->getNotifyType(),
700 'NOTIFY_MODULE' =>
$message->getNotifyModule(),
701 'NOTIFY_EVENT' =>
$message->getNotifyEvent(),
702 'NOTIFY_TAG' =>
$message->getNotifyTag(),
703 'NOTIFY_SUB_TAG' =>
$message->getNotifySubTag(),
704 'NOTIFY_TITLE' =>
$message->getNotifyTitle(),
705 'NOTIFY_BUTTONS' =>
$message->getNotifyButtons(),
706 'NOTIFY_READ' =>
$message->isNotifyRead(),
707 'IMPORT_ID' =>
$message->getImportId(),
708 'PARAMS' =>
$message->getParams()->toRestFormat(),
709 'MESSAGE_TYPE' => $this->chat->getType(),
710 'CHAT_AUTHOR_ID'=> $this->chat->getAuthorId(),
711 'CHAT_ENTITY_TYPE' => $this->chat->getEntityType(),
712 'CHAT_ENTITY_ID' => $this->chat->getEntityId(),
713 'CHAT_PARENT_ID' => $this->chat->getParentChatId(),
714 'CHAT_PARENT_MID' => $this->chat->getParentMessageId(),
715 'CHAT_ENTITY_DATA_1' => $this->chat->getEntityData1(),
716 'CHAT_ENTITY_DATA_2' => $this->chat->getEntityData2(),
717 'CHAT_ENTITY_DATA_3' => $this->chat->getEntityData3(),
718 'DATE_MODIFY' =>
new DateTime(),
721 if ($this->chat instanceof Chat\PrivateChat)
723 $authorId =
$message->getAuthorId();
724 $messageForEvent[
'FROM_USER_ID'] = $authorId;
725 $messageForEvent[
'TO_USER_ID'] = $this->chat->getCompanion($authorId)->getId() ?: $authorId;
729 $messageForEvent[
'BOT_IN_CHAT'] = $this->chat->getBotInChat();
732 return $messageForEvent;
737 $this->lastMessageViewers ??= $this->chat->getLastMessageViewsByGroups();
739 if (isset($this->lastMessageViewers[
'USERS'][
$userId]))
741 return Common::toJson($this->lastMessageViewers[
'FOR_VIEWERS'] ?? []);
744 return Common::toJson($this->lastMessageViewers[
'FOR_NOT_VIEWERS'] ?? []);
749 $this->messages->setContext(
$context);
752 return $this->defaultSetContext(
$context);
755 private function sendAnalyticsData(): void
757 foreach ($this->messages as
$message)
759 $messageType = $this->getMessageComponentName(
$message->getId());
763 private function getMessageComponentName(
int $id): string
765 if (!isset($this->messages[$id]))
769 $this->messages->fillParams();
771 return (
new MessageContent($this->messages[$id]))->getComponentName();
774 private function deleteAnchors(): void
776 $readService = AnchorContainer::getInstance()->getReadService()->setContext($this->getContext());
778 $readService->readByMessageIds($this->messages->getIds());