Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/im/lib/ |
Upload File : |
<?php namespace Bitrix\Im; use Bitrix\Im\Model\MessageTable; use Bitrix\Im\V2\Message; use Bitrix\Im\V2\Message\CounterService; use Bitrix\Im\V2\Message\Params; use Bitrix\Im\V2\Message\ReadService; use Bitrix\Im\V2\MessageCollection; use Bitrix\Im\V2\Result; use Bitrix\Im\V2\Notification\Group\Condition\ConditionFactory; use Bitrix\Im\V2\Notification\Group\Condition\Conditions; use Bitrix\Main\Type\DateTime; use CIMMessageParam; use CIMNotify; class Notify { public const EVENT_DEFAULT = 'default', EVENT_SYSTEM = 'system', EVENT_GROUP = 'group', EVENT_PRIVATE = 'private', EVENT_PRIVATE_SYSTEM = 'private_system' ; private const CONFIRM_TYPE = 1; private const SIMPLE_TYPE = 3; private const ALL_TYPES = 4; private $convertText; private $pageLimit; private $lastType; private $lastId; private $chatId; private $users = []; private $firstPage; private $searchText; private $searchType; private $searchDate; private $totalCount; private string $groupTag; private int $userId; private Conditions $groupConditions; public function __construct($options = []) { $this->convertText = $options['CONVERT_TEXT'] ?? null; $this->searchText = $options['SEARCH_TEXT'] ?? null; $this->searchType = $options['SEARCH_TYPE'] ?? null; $this->searchDate = $options['SEARCH_DATE'] ?? null; $this->pageLimit = $options['LIMIT'] ?? null; $this->lastType = $options['LAST_TYPE'] ?? null; $this->lastId = $options['LAST_ID'] ?? null; $this->groupTag = (string)($options['GROUP_TAG'] ?? ''); $this->firstPage = !$this->lastId && !$this->lastType; $this->userId = (int)\Bitrix\Im\Common::getUserId(); $chatData = $this->getChatData(); if ($chatData !== null) { $this->chatId = (int)$chatData['CHAT_ID']; $this->totalCount = (int)$chatData['IM_MODEL_RELATION_CHAT_MESSAGE_COUNT']; } } private function getChatData(): ?array { $userId = \Bitrix\Im\Common::getUserId(); if (!$userId) { return null; } $chatData = \Bitrix\Im\Model\RelationTable::getList([ 'select' => ['CHAT_ID', 'CHAT.MESSAGE_COUNT'], 'filter' => [ '=USER_ID' => $userId, '=MESSAGE_TYPE' => 'S' ] ])->fetch(); if (!$chatData) { return null; } return $chatData; } public static function getRealCounter($chatId): int { return self::getCounters($chatId, true)[$chatId]; } public static function getRealCounters($chatId) { return self::getCounters($chatId, true); } public static function getCounter($chatId): int { return self::getCounters($chatId)[$chatId]; } public static function getCounters($chatId, $isReal = false) { $result = Array(); $chatList = Array(); if (is_array($chatId)) { foreach($chatId as $id) { $id = intval($id); if ($id) { $result[$id] = 0; $chatList[$id] = $id; } } $chatList = array_values($chatList); $isMulti = count($chatList) > 1; } else { $id = intval($chatId); if ($id) { $result[$id] = 0; $chatList[] = $id; } $isMulti = false; } if (!$chatList) { return false; } /*if ($isReal) { $query = " SELECT CHAT_ID, COUNT(1) COUNTER FROM b_im_message WHERE CHAT_ID ".($isMulti? ' IN ('.implode(',', $chatList).')': ' = '.$chatList[0])." AND NOTIFY_READ = 'N' GROUP BY CHAT_ID "; } else { $query = " SELECT CHAT_ID, COUNTER FROM b_im_relation WHERE CHAT_ID ".($isMulti? ' IN ('.implode(',', $chatList).')': ' = '.$chatList[0])." "; }*/ /*$orm = \Bitrix\Main\Application::getInstance()->getConnection()->query($query); while($row = $orm->fetch()) { $result[$row['CHAT_ID']] = (int)$row['COUNTER']; }*/ if ($isMulti) { $result = (new CounterService(Common::getUserId()))->getForNotifyChats($chatList); } else { $counter = (new CounterService(Common::getUserId()))->getByChat($chatList[0]); $result[$chatList[0]] = $counter; } return $result; } public function get() { if (!$this->chatId || !$this->totalCount) { return [ 'notifications' => [], 'users' => [], ]; } // fetching confirm notifications $confirmCollection = $this->fetchConfirms(); // fetching simple notifications $offset = count($confirmCollection); $simpleCollection = $this->fetchSimple($offset); $notifications = array_merge($confirmCollection, $simpleCollection); /*$unreadCount = \Bitrix\Im\Model\MessageTable::getList( [ 'select' => ['CNT'], 'filter' => [ '=CHAT_ID' => $this->chatId, '=NOTIFY_READ' => 'N' ], 'runtime' => [ new \Bitrix\Main\ORM\Fields\ExpressionField('CNT', 'COUNT(*)') ] ] )->fetch();*/ $unreadCount = (new CounterService(\Bitrix\Im\Common::getUserId()))->getByChat($this->chatId); $result = [ 'TOTAL_COUNT' => $this->totalCount, 'TOTAL_UNREAD_COUNT' => (int)$unreadCount, 'CHAT_ID' => $this->chatId, 'NOTIFICATIONS' => $notifications, 'USERS' => $this->users, ]; foreach ($result['NOTIFICATIONS'] as $key => $value) { if ($value['DATE'] instanceof DateTime) { $result['NOTIFICATIONS'][$key]['DATE'] = date('c', $value['DATE']->getTimestamp()); } $result['NOTIFICATIONS'][$key] = array_change_key_case($result['NOTIFICATIONS'][$key], CASE_LOWER); } $result['NOTIFICATIONS'] = array_values($result['NOTIFICATIONS']); $result['USERS'] = array_values($result['USERS']); $result = array_change_key_case($result, CASE_LOWER); return $result; } private function requestData(int $requestType, int $limit): array { $collection = []; $ormParams = $this->prepareGettingIdParams($requestType, $limit); $ids = \Bitrix\Im\Model\MessageTable::getList($ormParams)->fetchAll(); if (count($ids) === 0) { return $collection; } $ids = array_map(static function($item) { return (int)$item['ID']; }, $ids); $ormParams = $this->prepareFilteringByIdParams($ids); $ormResult = \Bitrix\Im\Model\MessageTable::getList($ormParams); foreach ($ormResult as $notifyItem) { if ($notifyItem['NOTIFY_EVENT'] === self::EVENT_PRIVATE_SYSTEM) { $notifyItem['AUTHOR_ID'] = 0; } $collection[$notifyItem['ID']] = [ 'ID' => (int)$notifyItem['ID'], 'CHAT_ID' => $this->chatId, 'AUTHOR_ID' => (int)$notifyItem['AUTHOR_ID'], 'DATE' => $notifyItem['DATE_CREATE'], 'NOTIFY_TYPE' => (int)$notifyItem['NOTIFY_TYPE'], 'NOTIFY_MODULE' => $notifyItem['NOTIFY_MODULE'], 'NOTIFY_EVENT' => $notifyItem['NOTIFY_EVENT'], 'NOTIFY_TAG' => $notifyItem['NOTIFY_TAG'], 'NOTIFY_SUB_TAG' => $notifyItem['NOTIFY_SUB_TAG'], 'NOTIFY_TITLE' => $notifyItem['NOTIFY_TITLE'], //'NOTIFY_READ' => $notifyItem['NOTIFY_READ'], 'SETTING_NAME' => $notifyItem['NOTIFY_MODULE'].'|'.$notifyItem['NOTIFY_EVENT'], ]; $collection[$notifyItem['ID']]['TEXT'] = \Bitrix\Im\Text::parse( \Bitrix\Im\Text::convertHtmlToBbCode($notifyItem['MESSAGE']), ['LINK_TARGET_SELF' => 'Y'] ); if ($notifyItem['AUTHOR_ID'] && !isset($this->users[$notifyItem['AUTHOR_ID']])) { $user = User::getInstance($notifyItem['AUTHOR_ID'])->getArray([ 'JSON' => 'Y', 'SKIP_ONLINE' => 'Y' ]); $user['last_activity_date'] = $notifyItem['USER_LAST_ACTIVITY_DATE'] ? date('c', $notifyItem['USER_LAST_ACTIVITY_DATE']->getTimestamp()) : false ; $user['desktop_last_date'] = false; $user['mobile_last_date'] = false; $user['idle'] = false; $this->users[$notifyItem['AUTHOR_ID']] = $user; } //keyboard creation if ($notifyItem['NOTIFY_BUTTONS']) { $buttons = unserialize($notifyItem['NOTIFY_BUTTONS'], ['allowed_classes' => false]); $keyboard = new \Bitrix\Im\Bot\Keyboard(111); $command = 'notifyConfirm'; foreach ($buttons as $button) { $keyboard->addButton( [ 'TEXT' => $button['TITLE'], 'COMMAND' => $command, 'COMMAND_PARAMS' => $notifyItem['ID'].'|'.$button['VALUE'], 'TEXT_COLOR' => '#fff', 'BG_COLOR' => $button['TYPE'] === 'accept' ? '#8BC84B' : '#ef4b57', 'DISPLAY' => 'LINE' ] ); } $collection[$notifyItem['ID']]['NOTIFY_BUTTONS'] = $keyboard->getJson(); } } if (count($collection) > 0) { $params = \CIMMessageParam::Get(array_keys($collection)); foreach ($params as $notificationId => $param) { $collection[$notificationId]['PARAMS'] = empty($param) ? null : $param; } $collection = $this->fillReadStatuses($collection); } return $collection; } private function fetchConfirms(): array { $confirmCollection = []; $nextPageIsConfirm = $this->lastType === self::CONFIRM_TYPE; if ($this->firstPage || $nextPageIsConfirm) { $confirmCollection = $this->requestData(self::CONFIRM_TYPE, $this->pageLimit); } return $confirmCollection; } private function fetchSimple(int $offset): array { $simpleCollection = []; $nextPageIsSimple = $this->lastType === self::SIMPLE_TYPE; $needMoreOnFirstPage = $this->firstPage && $offset < $this->pageLimit; $notEnoughFromPreviousStep = $this->lastType === self::CONFIRM_TYPE && $offset < $this->pageLimit; if ($needMoreOnFirstPage || $notEnoughFromPreviousStep || $nextPageIsSimple) { $simpleCollection = $this->requestData(self::SIMPLE_TYPE, $this->pageLimit - $offset); } return $simpleCollection; } public function search(): array { if (!$this->chatId) { return []; } if (!$this->searchText && !$this->searchType && !$this->searchDate) { return []; } if ($this->lastId > 0) { $this->lastType = self::ALL_TYPES; $this->firstPage = false; } // fetching searched notifications $collection = $this->requestData(self::ALL_TYPES, $this->pageLimit); $result = [ 'CHAT_ID' => $this->chatId, 'NOTIFICATIONS' => $collection, 'USERS' => $this->users, ]; if (!$this->lastId) { $result['TOTAL_RESULTS'] = $this->requestSearchTotalCount(); } foreach ($result['NOTIFICATIONS'] as $key => $value) { if ($value['DATE'] instanceof DateTime) { $result['NOTIFICATIONS'][$key]['DATE'] = date('c', $value['DATE']->getTimestamp()); } $result['NOTIFICATIONS'][$key] = array_change_key_case($result['NOTIFICATIONS'][$key], CASE_LOWER); } $result['NOTIFICATIONS'] = array_values($result['NOTIFICATIONS']); $result['USERS'] = array_values($result['USERS']); $result = array_change_key_case($result, CASE_LOWER); return $result; } /** * Agent for deleting old notifications. * * @return string */ public static function cleanNotifyAgent(): string { $dayCount = 60; $limit = 2000; $step = 1000; $batches = []; $result = \Bitrix\Im\Model\MessageTable::getList([ 'select' => ['ID', 'CHAT_ID'], 'filter' => [ '=NOTIFY_TYPE' => [IM_NOTIFY_CONFIRM, IM_NOTIFY_FROM, IM_NOTIFY_SYSTEM], '<DATE_CREATE' => ConvertTimeStamp((time() - 86400 * $dayCount), 'FULL') ], 'limit' => $limit ]); $batch = new MessageCollection(); $i = 0; while ($row = $result->fetch()) { if ($i++ === $step) { $i = 0; $batches[] = $batch; $batch = new MessageCollection(); } $message = (new Message())->setMessageId((int)$row['ID'])->setChatId((int)$row['CHAT_ID']); $batch->add($message); } if ($batch->count() !== 0) { $batches[] = $batch; } $counterService = new CounterService(); foreach ($batches as $batch) { $messageIds = $batch->getIds(); if (empty($messageIds)) { continue; } \Bitrix\Im\Model\MessageTable::deleteBatch([ '=ID' => $messageIds ]); \Bitrix\Im\Model\MessageParamTable::deleteBatch([ '=MESSAGE_ID' => $messageIds ]); $counterService->deleteByMessagesForAll($batch); } return __METHOD__. '();'; } private function requestSearchTotalCount(): int { return \Bitrix\Im\Model\MessageTable::getCount($this->getFilterConditions()); } /** * Generates params for GetList to get only IDs of the necessary notifications with filters. * * @param int $requestType Notification type. * @param int $limit Amount of requested notifications. * * @return array * @throws \Bitrix\Main\ObjectException */ private function prepareGettingIdParams(int $requestType, int $limit): array { return [ 'select' => ['ID'], 'filter' => $this->getFilterConditions($requestType, true), 'order' => ['DATE_CREATE' => 'DESC', 'ID' => 'DESC'], 'limit' => $limit ]; } private function getFilterConditions(int $requestType = self::ALL_TYPES, bool $withIdCondition = false): array { $filter = [ '=CHAT_ID' => $this->chatId, ]; if ($requestType === self::CONFIRM_TYPE) { $filter['=NOTIFY_TYPE'] = IM_NOTIFY_CONFIRM; } elseif ($requestType === self::SIMPLE_TYPE) { $filter['!=NOTIFY_TYPE'] = IM_NOTIFY_CONFIRM; } elseif ($requestType === self::ALL_TYPES) { if ($this->searchText) { $filter['*%MESSAGE'] = $this->searchText; } if ($this->searchType) { $options = explode('|', $this->searchType); $filter['=NOTIFY_MODULE'] = $options[0]; if (isset($options[1])) { $filter['=NOTIFY_EVENT'] = $options[1]; } } if ($this->searchDate) { $dateStart = new DateTime( $this->searchDate, \DateTimeInterface::RFC3339, new \DateTimeZone('UTC') ); $dateEnd = ( new DateTime( $this->searchDate, \DateTimeInterface::RFC3339, new \DateTimeZone('UTC') ) )->add('1 DAY'); $filter['><DATE_CREATE'] = [$dateStart, $dateEnd]; } if ($this->groupTag) { $filter = array_merge($filter, $this->getGroupConditions()->toFilterFormat()); } } if ($withIdCondition && !$this->firstPage) { if ( $requestType === self::CONFIRM_TYPE || ($requestType === self::SIMPLE_TYPE && $this->lastType === self::SIMPLE_TYPE) || ($requestType === self::ALL_TYPES && $this->lastType === self::ALL_TYPES) ) { $filter['<ID'] = $this->lastId; } } return $filter; } /** * Generates params for GetList to get all the necessary notification data filtering by notifications IDs. * * @param int[] $ids Notification IDs. * * @return array */ private function prepareFilteringByIdParams(array $ids): array { return [ 'select' => [ 'ID', 'AUTHOR_ID', 'MESSAGE', 'DATE_CREATE', 'NOTIFY_TYPE', 'NOTIFY_EVENT', 'NOTIFY_MODULE', 'NOTIFY_TAG', 'NOTIFY_SUB_TAG', 'NOTIFY_TITLE', //'NOTIFY_READ', 'NOTIFY_BUTTONS', 'USER_LAST_ACTIVITY_DATE' => 'AUTHOR.LAST_ACTIVITY_DATE', //'USER_IDLE' => 'STATUS.IDLE', //'USER_MOBILE_LAST_DATE' => 'STATUS.MOBILE_LAST_DATE', //'USER_DESKTOP_LAST_DATE' => 'STATUS.DESKTOP_LAST_DATE', ], 'filter' => ['=ID' => $ids], 'order' => ['DATE_CREATE' => 'DESC', 'ID' => 'DESC'], ]; } public function getLastId(): ?int { if (!$this->chatId) { return null; } $ormParams = [ 'select' => ['ID'], 'filter' => ['=CHAT_ID' => $this->chatId], 'order' => ['DATE_CREATE' => 'DESC', 'ID' => 'DESC'], 'limit' => 1, ]; $getListResult = \Bitrix\Im\Model\MessageTable::getList($ormParams)->fetch(); if (!$getListResult) { return null; } if (count($getListResult) === 1) { return (int)$getListResult['ID']; } return null; } private function fillReadStatuses(array $notifications): array { $messageIds = array_keys($notifications); $readStatuses = (new ReadService(\Bitrix\Im\Common::getUserId()))->getReadStatusesByMessageIds($messageIds); foreach ($notifications as $id => $notification) { $notifications[$id]['NOTIFY_READ'] = $readStatuses[$id] ? 'Y' : 'N'; } return $notifications; } private function getGroupConditions(): Conditions { if (!isset($this->groupConditions)) { $this->groupConditions = (new ConditionFactory())->makeByTag($this->groupTag, $this->userId); } return $this->groupConditions; } public static function deleteOldNotifyByTag(int $currentMessageId, array $arParams): Result { $result = new Result(); $notifyTag = (string)$arParams['NOTIFY_TAG']; $chatId = (int)$arParams['CHAT_ID']; if (!$notifyTag || $chatId <= 0) { return $result; } $query = MessageTable::query() ->setSelect(['ID', 'AUTHOR_ID', 'CHAT_ID']) ->where('NOTIFY_TAG', $notifyTag) ; if ((int)$arParams['NOTIFY_TYPE'] !== IM_NOTIFY_CONFIRM) { $query->where('CHAT_ID', $chatId); } $query = $query->exec(); $messages = []; $messageIds = []; $maxId = 0; while ($row = $query->fetch()) { $messageId = (int)$row['ID']; $messageIds[$messageId] = $messageId; if ((int)$row['CHAT_ID'] !== $chatId) { continue; } $messages[$messageId] = [ 'ID' => $messageId, 'AUTHOR_ID' => (int)$row['AUTHOR_ID'], 'CHAT_ID' => (int)$row['CHAT_ID'], ]; if ($messageId > $maxId) { $maxId = $messageId; $maxAuthorId = (int)$row['AUTHOR_ID']; } } $params = self::getUsersParam(array_keys($messages)); $newParams = []; foreach ($messages as $message) { $newParams[$message['AUTHOR_ID']] = (int)$message['AUTHOR_ID']; foreach ($params[$message['ID']] ?? [] as $param) { $newParams[$param] = (int)$param; } } unset($messageIds[$maxId]); CIMNotify::deleteList($messageIds, ['NOTIFY_TAG' => $notifyTag]); if ($currentMessageId >= $maxId) { unset($newParams[$arParams['AUTHOR_ID']]); } else { unset($newParams[$maxAuthorId ?? 0]); CIMMessageParam::Set($maxId, ['USERS' => array_values($newParams)]); } return $currentMessageId < $maxId ? $result : $result->setResult(array_values($newParams)); } private static function getUsersParam(array $messageIds): array { if (empty($messageIds)) { return []; } $query = \Bitrix\Im\Model\MessageParamTable::query() ->setSelect(['MESSAGE_ID', 'PARAM_VALUE']) ->whereIn('MESSAGE_ID', $messageIds) ->where('PARAM_NAME', Params::USERS) ->exec() ; $params = []; while ($row = $query->fetch()) { $params[$row['MESSAGE_ID']][] = $row['PARAM_VALUE']; } return $params; } }