41 private const MESSAGE_OWN_OTHER = 4;
56 private ?array $chatLastMessage =
null;
57 private ?
int $chatPrevMessageId =
null;
59 private bool $needUpdateRecent =
false;
60 private array $counters;
61 private array $lastMessageViewers;
62 private int $previousMessageId;
66 $this->message = $message;
68 $this->chat = Chat\ChatFactory::getInstance()->getChatById($this->message->getChatId());
73 $this->message = $message;
75 $this->chat = Chat\ChatFactory::getInstance()->getChatById($this->message->getChatId());
86 if (in_array($mode, [self::MODE_AUTO, self::MODE_SOFT, self::MODE_HARD, self::MODE_COMPLETE],
true))
99 $messageFields = $this->message->toArray();
100 $messageFields[
'PARAMS'] = $this->message->getParams()->toRestFormat();
101 $messageFields[
'CHAT_ENTITY_TYPE'] = $this->chat->getEntityType();
102 $messageFields[
'CHAT_ENTITY_ID'] = $this->chat->getEntityId();
109 $files = $this->message->getFiles();
114 $result = $this->deleteSoft();
115 $this->fireEventAfterMessageDelete($messageFields);
118 $this->getChatPreviousMessages();
119 $result = $this->deleteHard();
120 $this->fireEventAfterMessageDelete($messageFields,
true);
123 $this->getChatPreviousMessages();
124 $result = $this->deleteHard(
true);
125 $this->fireEventAfterMessageDelete($messageFields,
true);
131 if (Option::get(
'im',
'message_history_index'))
133 MessageIndexTable::delete($this->message->getId());
136 (
new UrlService())->deleteUrlsByMessage($this->message);
137 foreach ($files as $file)
139 $file->getDiskFile()->delete(SystemUser::SYSTEM_USER_ID);
156 if ($this->getContext()->getUser()->isSuperAdmin())
161 $userId = $this->getContext()->getUserId();
164 if (!$this->chat->hasAccess($userId))
170 $removerRole = self::ROLE_USER;
171 if ($userId === $this->chat->getAuthorId())
175 $removerRole = self::ROLE_OWNER;
180 $relation = $this->chat->getSelfRelation();
181 if ($relation && $relation->getManager())
183 $removerRole = self::ROLE_MANAGER;
185 elseif ($relation->getUser()->getExternalAuthId() === Bot::EXTERNAL_AUTH_ID)
192 $messageOwner = self::MESSAGE_OWN_OTHER;
193 if ($messageAuthor = $this->message->getAuthor())
195 if ($messageAuthor->getId() === $userId)
197 $messageOwner = self::MESSAGE_OWN_SELF;
199 elseif($messageAuthor->getId() === $this->chat->getAuthorId())
201 $messageOwner = self::ROLE_OWNER;
205 $relations = $this->chat->getRelations([
'USER_ID' => $messageAuthor->getId()]);
206 if ($user = $relations->getByUserId($messageAuthor->getId(), $this->chat->getChatId()))
208 $messageOwner = self::ROLE_USER;
209 if ($user->getManager())
211 $messageOwner = self::ROLE_MANAGER;
217 if ($removerRole <= $messageOwner)
223 $messageAuthor === self::ROLE_OWNER
231 if ($this->message->isViewedByOthers())
239 private function deleteSoft():
Result
241 $date = FormatDate(
'FULL', $this->message->getDateCreate()->getTimestamp() + \CTimeZone::GetOffset());
244 $this->message->setMessageOut(
Loc::getMessage(
'IM_MESSAGE_DELETED_OUT', [
'#DATE#' => $date]));
245 $this->message->resetParams([
248 $this->message->save();
251 Sync\Logger::getInstance()->add(
252 new Sync\
Event(Sync\Event::DELETE_EVENT, Sync\Event::MESSAGE_ENTITY, $this->message->getId()),
253 fn () => $this->chat->getRelations()->getUserIds()
257 $this->sendPullMessage();
262 private function deleteHard($removeAny =
false):
Result
266 $deleteAfter = \COption::GetOptionInt(
'im', self::OPTION_KEY_DELETE_AFTER);
267 if ($deleteAfter > $this->message->getDateCreate()->getTimestamp())
273 $this->deleteLinks();
274 $this->recountChat();
275 $this->sendPullMessage(
true);
276 $this->message->delete();
277 if (!$this->chat instanceof Chat\OpenLineChat)
279 Sync\Logger::getInstance()->add(
280 new Sync\Event(Sync\Event::COMPLETE_DELETE_EVENT, Sync\Event::MESSAGE_ENTITY, $this->message->getId()),
281 fn () => $this->chat->getRelations()->getUserIds()
288 private function sendPullMessage(
bool $completeDelete =
false): Result
292 if ($this->chat instanceof Chat\PrivateChat)
294 $userId = $this->message->getAuthorId();
295 $companionUserId = $this->chat->getCompanion($userId)->getId();
296 $this->sendPullMessagePrivate($userId, $companionUserId, $pullMessage, $completeDelete);
297 $this->sendPullMessagePrivate($companionUserId, $userId, $pullMessage, $completeDelete);
301 $groupedPullMessage = $this->groupPullByCounter($pullMessage, $completeDelete);
302 foreach ($groupedPullMessage as $pullForGroup)
304 Event::add($pullForGroup[
'users'], $pullForGroup[
'event']);
309 \CPullWatch::AddToStack(
'IM_PUBLIC_' . $this->chat->getChatId(), $pullMessage);
316 private function sendPullMessagePrivate(
int $fromUser,
int $toUser, array $pullMessage,
bool $completeDelete): void
319 $relation = $this->chat->getRelations()->getByUserId($toUser, $this->chat->getChatId());
320 if ($relation !==
null)
322 $isMuted = $relation->getNotifyBlock() ??
false;
324 $pullMessage[
'params'][
'dialogId'] = $fromUser;
325 $pullMessage[
'params'][
'fromUserId'] = $fromUser;
326 $pullMessage[
'params'][
'toUserId'] = $toUser;
327 $pullMessage[
'params'][
'counter'] = $this->getCounter($toUser);
328 $pullMessage[
'params'][
'unread'] =
Recent::isUnread($toUser, $this->chat->getType(), $fromUser);
329 $pullMessage[
'params'][
'muted'] = $isMuted;
330 if ($completeDelete && $this->needUpdateRecent)
332 $pullMessage[
'params'][
'lastMessageViews'] = $this->getLastViewers($toUser);
334 Event::add($toUser, $pullMessage);
340 'id' => (int)$this->message->getId(),
343 'senderId' => $this->message->getAuthorId(),
344 'params' => [
'IS_DELETED' =>
'Y',
'URL_ID' => [],
'FILE_ID' => [],
'KEYBOARD' =>
'N',
'ATTACH' => []],
345 'chatId' => $this->chat->getChatId(),
350 $params[
'dialogId'] = $this->chat->getDialogId();
353 if ($completeDelete && $this->needUpdateRecent)
355 if ($this->chatLastMessage[
'ID'] !== 0)
357 $newLastMessage =
new Message($this->chatLastMessage[
'ID']);
358 if ($newLastMessage->getId())
360 $params[
'newLastMessage'] = $this->formatNewLastMessage($newLastMessage);
365 $params[
'newLastMessage'] = [
'id' => 0];
371 'command' => $completeDelete ?
'messageDeleteComplete' :
'messageDelete',
373 'push' => $completeDelete ? [
'badge' =>
'Y'] : [],
378 private function groupPullByCounter(array $pullMessage,
bool $completeDelete): array
382 $relations = $this->chat->getRelations();
383 $unreadList =
Recent::getUnread($this->chat->getType(), $this->chat->getDialogId());
384 foreach ($relations as $relation)
386 $user = $relation->getUser();
388 (!$user->isActive() && $user->getExternalAuthId() !== \
Bitrix\Im\Bot::EXTERNAL_AUTH_ID)
389 || ($this->chat->getEntityType() ===
Chat::ENTITY_TYPE_LINE && $user->getExternalAuthId() ===
'imconnector')
395 $userId = $relation->getUserId();
397 $pullMessage[
'params'][
'unread'] = $unreadList[$userId] ??
false;
398 $pullMessage[
'params'][
'muted'] = $relation->getNotifyBlock() ??
false;
400 $events[$userId] = $pullMessage;
403 if ($this->needUpdateRecent && $completeDelete)
405 $lastMessageViews = $this->getLastViewers($userId);
406 $events[$userId][
'params'][
'lastMessageViews'] = $lastMessageViews;
407 $count = $lastMessageViews[
'countOfViewers'] ?? 0;
410 $unreadGroupFlag = $pullMessage[
'params'][
'unread'] ? 1 : 0;
411 $mutedGroupFlag = $pullMessage[
'params'][
'muted'] ? 1 : 0;
413 $events[$userId][
'params'][
'counter'] = $this->getCounter($userId);
414 $events[$userId][
'groupId'] =
416 . $this->chat->getChatId()
417 .
'_'. $this->message->getMessageId()
418 .
'_'. $events[$userId][
'params'][
'counter']
420 .
'_'. $unreadGroupFlag
421 .
'_'. $mutedGroupFlag
425 return (
new Message\Send\PushService())->getEventByCounterGroup($events);
428 private function deleteLinks()
433 $childChatResult = Chat\ChatFactory::getInstance()->findChat([
'PARENT_MID' => $this->message->getId()]);
434 if ($childChatResult->hasResult())
436 $childChat = Chat\ChatFactory::getInstance()->getChat($childChatResult->getResult());
437 $childChat->deleteChat();
440 (new \Bitrix\Im\V2\Link\Favorite\FavoriteService())->unmarkMessageAsFavoriteForAll($this->message);
441 (new \Bitrix\Im\V2\Message\ReadService())->deleteByMessageId(
442 $this->message->getMessageId(),
443 $this->chat->getRelations()->getUserIds()
445 $this->message->unpin();
447 if (Loader::includeModule(
'tasks'))
450 if ($taskItem !==
null)
452 $taskItem->setMessageId(0);
453 (
new TaskService())->updateTaskLink($taskItem);
457 if (Loader::includeModule(
'calendar'))
459 $calendarItem = CalendarItem::getByMessageId($this->message->getMessageId());
460 if ($calendarItem !==
null)
462 $calendarItem->setMessageId(0);
463 (
new CalendarService())->updateCalendarLink($calendarItem);
467 $this->message->getParams()->delete();
470 $tablesToDeleteRow = [
472 'b_im_message_favorite',
473 'b_im_message_disappearing',
474 'b_im_message_index',
475 'b_im_link_reminder',
476 'b_imconnectors_delivery_mark',
479 foreach ($tablesToDeleteRow as $table)
481 $connection->query(
"DELETE FROM " . $table .
" WHERE MESSAGE_ID = " . $this->message->getId());
485 private function fireEventAfterMessageDelete(array $messageFields,
bool $completeDelete =
false): Result
487 $result =
new Result;
489 foreach(GetModuleEvents(
'im', self::EVENT_AFTER_MESSAGE_DELETE,
true) as $event)
492 'ID' => $messageFields[
'ID'],
494 'COMPLETE_DELETE' => $completeDelete,
498 ExecuteModuleEventEx($event, [$messageFields[
'ID'], $messageFields, $deleteFlags]);
504 private function recountChat(): void
506 $this->updateRecent();
507 if (!is_null($this->chatLastMessage))
509 $isMessageRead = !!MessageViewedTable::query()
510 ->addFilter(
'MESSAGE_ID', $this->chatLastMessage[
'ID'])
513 $this->chat->setLastMessageId((
int)($this->chatLastMessage[
'ID'] ?? 0));
514 $this->chat->setLastMessageStatus($isMessageRead ? \IM_MESSAGE_STATUS_DELIVERED : \IM_MESSAGE_STATUS_RECEIVED);
517 $this->chat->setPrevMessageId($this->chatPrevMessageId ?? 0);
519 $this->chat->setMessageCount($this->chat->getMessageCount() - 1);
521 $this->updateRelation();
524 private function updateRelation(): void
526 if ($this->needUpdateRecent)
528 $newLastId = $this->chatLastMessage[
'ID'] ?? 0;
532 $newLastId = $this->getPreviousMessageId();
536 SET LAST_ID = {$newLastId}
537 WHERE CHAT_ID = {$this->message->getChatId()} AND LAST_ID = {$this->message->getMessageId()}
541 private function updateRecent(): void
543 if ($this->chatLastMessage && (
int)$this->chatLastMessage[
'ID'] !== $this->message->getId())
546 'DATE_MESSAGE' => $this->chatLastMessage[
'DATE_CREATE'],
547 'DATE_UPDATE' => $this->chatLastMessage[
'DATE_CREATE'],
548 'ITEM_MID' => $this->chatLastMessage[
'ID'] ?? 0,
554 $opponentId = $this->chat->getCompanion($userId)->getId();
555 RecentTable::updateByFilter(
557 '=USER_ID' => $userId,
559 '=ITEM_ID' => $opponentId
563 RecentTable::updateByFilter(
565 '=USER_ID' => $opponentId,
567 '=ITEM_ID' => $userId
574 RecentTable::updateByFilter(
575 [
'=ITEM_TYPE' => $this->chat->getType(),
'=ITEM_ID' => $this->chat->getId()],
582 private function getCounter(
int $userId): int
584 $this->counters ??= (
new Message\CounterService())
585 ->getByChatForEachUsers($this->chat->getChatId(), $this->chat->getRelations()->getUserIds())
588 return $this->counters[
$userId] ?? 0;
591 private function formatNewLastMessage(Message $message): array
598 if ($message->getFiles()->count() <= 0)
603 $file = $message->getFiles()->getAny();
610 $result[
'file'] = [
'type' => $file->getContentType(),
'name' => $file->getDiskFile()->getName()];
615 private function getLastViewers(
int $userId): array
617 $this->lastMessageViewers ??= $this->chat->getLastMessageViewsByGroups();
619 if (isset($this->lastMessageViewers[
'USERS'][$userId]))
621 return Common::toJson($this->lastMessageViewers[
'FOR_VIEWERS'] ?? []);
624 return Common::toJson($this->lastMessageViewers[
'FOR_NOT_VIEWERS'] ?? []);
627 private function getPreviousMessageId(): int
629 if (isset($this->previousMessageId))
631 return $this->previousMessageId;
634 $result = MessageTable::query()
636 ->where(
'CHAT_ID', $this->chat->getChatId())
637 ->where(
'ID',
'<', $this->message->getMessageId())
638 ->setOrder([
'DATE_CREATE' =>
'DESC',
'ID' =>
'DESC'])
642 $this->previousMessageId = ($result && isset($result[
'ID'])) ? (
int)$result[
'ID'] : 0;
644 return $this->previousMessageId;
647 private function getChatPreviousMessages(): ?array
649 $lastChatMessageId = $this->chat->getLastMessageId();
650 $prevChatMessageId = $this->chat->getPrevMessageId();
654 $this->message->getId(),
664 return $this->message->toArray();
667 $lastMessages = MessageTable::query()
668 ->setSelect([
'ID',
'DATE_CREATE',
'MESSAGE'])
669 ->addFilter(
'CHAT_ID', $this->chat->getChatId())
670 ->setOrder([
'DATE_CREATE' =>
'DESC',
'ID' =>
'DESC'])
674 $this->chatPrevMessageId = (int)($lastMessages[2][
'ID'] ?? 0);
675 $nullMessage = [
'ID' => 0,
'DATE_CREATE' => (
new DateTime()),
'MESSAGE' =>
''];
676 if ($this->message->getId() === $lastChatMessageId)
678 $this->needUpdateRecent =
true;
679 $this->chatLastMessage = $lastMessages[1] ?? $nullMessage;
683 $this->chatLastMessage = $lastMessages[0] ?? $nullMessage;
686 return $this->chatLastMessage;