403Webshell
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 :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/im/lib/notify.php
<?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;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit