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/chat.php
<?php
namespace Bitrix\Im;

use Bitrix\Im\Model\BlockUserTable;
use Bitrix\Im\V2\Chat\Background\Background;
use Bitrix\Im\V2\Chat\EntityLink;
use Bitrix\Im\V2\Chat\GeneralChannel;
use Bitrix\Im\V2\Chat\TextField\TextFieldEnabled;
use Bitrix\Im\V2\Message\Counter\CounterType;
use Bitrix\Im\V2\Message\CounterService;
use Bitrix\Im\V2\Message\Delete\DisappearService;
use Bitrix\Im\V2\Message\ReadService;
use Bitrix\Im\V2\Recent\Config\RecentConfigManager;
use Bitrix\Main\Application;
use Bitrix\Main\Engine\Response\Converter;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Type\DateTime;

Loc::loadMessages(__FILE__);

class Chat
{
	const TYPE_SYSTEM = 'S';
	const TYPE_PRIVATE = 'P';
	const TYPE_OPEN = 'O';
	const TYPE_THREAD = 'T';
	const TYPE_GROUP = 'C';
	const TYPE_CHANNEL = 'N';
	const TYPE_OPEN_LINE = 'L';

	const STATUS_UNREAD = 0;
	const STATUS_NOTIFY = 1;
	const STATUS_READ = 2;

	const LIMIT_SEND_EVENT = 30;

	const FILTER_LIMIT = 50;

	public static function getTypes()
	{
		return \CIMChat::getGroupTypes();
	}

	public static function getType($chatData, bool $camelCase = true)
	{
		$messageType = $chatData["TYPE"] ?? $chatData["CHAT_TYPE"] ?? '';
		$entityType = $chatData["ENTITY_TYPE"] ?? $chatData["CHAT_ENTITY_TYPE"] ?? '';

		$messageType = trim($messageType);
		$entityType = trim($entityType);

		$chatId = null;
		if (isset($chatData['ID']))
		{
			$chatId = (int)$chatData['ID'];
		}
		else if (isset($chatData['CHAT_ID']))
		{
			$chatId = (int)$chatData['CHAT_ID'];
		}

		if ($messageType == IM_MESSAGE_PRIVATE)
		{
			$result = 'PRIVATE';
		}
		else if ($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT)
		{
			$result = 'COPILOT';
		}
		else if ($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_COLLAB)
		{
			$result = 'COLLAB';
		}
		else if ($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_CHANNEL)
		{
			$result = 'CHANNEL';
		}
		else if (
			($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_OPEN_CHANNEL && $entityType === \Bitrix\Im\V2\Chat::ENTITY_TYPE_GENERAL_CHANNEL)
			|| (isset($chatId) && $chatId === GeneralChannel::getGeneralChannelId())
		)
		{
			$result = 'GENERAL_CHANNEL';
		}
		else if ($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_OPEN_CHANNEL)
		{
			$result = 'OPEN_CHANNEL';
		}
		else if ($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_COMMENT)
		{
			$result = 'COMMENT';
		}
		else if (!empty($entityType))
		{
			$result = $entityType;
		}
		else if (
			($messageType === \Bitrix\Im\V2\Chat::IM_TYPE_OPEN && $entityType === \Bitrix\Im\V2\Chat::ENTITY_TYPE_GENERAL)
			|| ($chatId && $chatId === (int)\CIMChat::GetGeneralChatId())
		)
		{
			$result = 'GENERAL';
		}
		else
		{
			$result = $messageType == IM_MESSAGE_OPEN? 'OPEN': 'CHAT';
		}

		if ($camelCase)
		{
			$result = Converter::toJson()->process($result);
		}

		return htmlspecialcharsbx($result);
	}

	public static function getRelation($chatId, $params = [])
	{
		$chatId = intval($chatId);
		if ($chatId <= 0)
		{
			return false;
		}

		$connection = Application::getInstance()->getConnection();

		$selectFields = '';
		if (isset($params['SELECT']))
		{
			$params['SELECT'][] = 'ID';
			$params['SELECT'][] = 'USER_ID';
			$map = \Bitrix\Im\Model\RelationTable::getMap();
			foreach ($params['SELECT'] as $key => $value)
			{
				if (is_int($key) && isset($map[$value]))
				{
					$selectFields .= "R.{$value}, ";
					unset($map[$value]);
				}
				else if (!is_int($key) && isset($map[$key]))
				{
					$value = (string)$value;
					$selectFields .= "R.{$key} '{$connection->getSqlHelper()->forSql($value)}', ";
					unset($map[$value]);
				}
			}
		}
		if (!$selectFields)
		{
			$selectFields = 'R.*, ';
		}

		$withUserFields = false;
		if (isset($params['USER_DATA']) && $params['USER_DATA'] == 'Y')
		{
			$withUserFields = true;
			$list = Array('ACTIVE', 'EXTERNAL_AUTH_ID');
			foreach ($list as $key)
			{
				$selectFields .= "U.{$key} USER_DATA_{$key}, ";
			}
		}
		$skipUsers = false;
		$skipUserInactiveSql = '';
		if (isset($params['SKIP_INACTIVE_USER']) && $params['SKIP_INACTIVE_USER'] === 'Y')
		{
			$skipUsers = true;
			$skipUserInactiveSql = "AND U.ACTIVE = 'Y'";
		}

		$skipUserTypes = $params['SKIP_USER_TYPES'] ?? [];
		if (isset($params['SKIP_CONNECTOR']) && $params['SKIP_CONNECTOR'] === 'Y')
		{
			$skipUserTypes[] = 'imconnector';
		}

		$skipUserTypesSql = '';
		if (!empty($skipUserTypes))
		{
			$skipUsers = true;
			if (count($skipUserTypes) === 1)
			{
				$skipUserTypesSql = "AND (U.EXTERNAL_AUTH_ID != '".$connection->getSqlHelper()->forSql($skipUserTypes[0])."' OR U.EXTERNAL_AUTH_ID IS NULL)";
			}
			else
			{
				$skipUserTypes = array_map(function($type) use ($connection) {
					return $connection->getSqlHelper()->forSql($type);
				}, $skipUserTypes);

				$skipUserTypesSql = "AND (U.EXTERNAL_AUTH_ID NOT IN ('".implode("','", $skipUserTypes)."') OR U.EXTERNAL_AUTH_ID IS NULL)";
			}
		}

		$whereFields = '';
		if (isset($params['FILTER']))
		{
			$map = \Bitrix\Im\Model\RelationTable::getMap();
			foreach ($params['FILTER'] as $key => $value)
			{
				if (!isset($map[$key]))
				{
					continue;
				}

				if (is_int($value))
				{
				}
				else if (is_bool($value))
				{
					$value = $value? "'Y'": "'N'";
				}
				else if (is_string($value))
				{
					$value = "'{$connection->getSqlHelper()->forSql($value)}'";
				}
				else
				{
					continue;
				}

				$whereFields .= " AND R.{$key} = {$value}";
			}
		}

		$limit = '';
		if (isset($params['LIMIT']))
		{
			$limit = 'LIMIT '.(int)$params['LIMIT'];
		}

		$offset = '';
		if (isset($params['OFFSET']))
		{
			$offset = 'OFFSET '.(int)$params['OFFSET'];
		}

		$orderField = 'R.ID';

		if (isset($params['LAST_USER_ID']) && (int)$params['LAST_USER_ID'] >= 0)
		{
			$lastUserId = (int)$params['LAST_USER_ID'];
			$whereFields .= " AND R.USER_ID > {$lastUserId}";
			$orderField = 'R.USER_ID';
		}

		$selectFields = rtrim($selectFields, ', ');
		$sql = "
			SELECT {$selectFields}
			FROM b_im_relation R
			".($withUserFields && !$skipUsers? "LEFT JOIN b_user U ON R.USER_ID = U.ID": "")."
			".($skipUsers? "INNER JOIN b_user U ON R.USER_ID = U.ID {$skipUserInactiveSql} {$skipUserTypesSql}": "")."
			WHERE R.CHAT_ID = {$chatId} {$whereFields} 
			ORDER BY {$orderField} ASC
			{$limit} {$offset}
		";
		$relations = array();
		$query = $connection->query($sql);
		while ($row = $query->fetch())
		{
			foreach ($row as $key => $value)
			{
				if (mb_strpos($key, 'USER_DATA_') === 0)
				{
					$row['USER_DATA'][mb_substr($key, 10)] = $value;
					unset($row[$key]);
				}
			}

			$relations[$row['USER_ID']] = $row;
		}

		$userIds = array_keys($relations);

		if (($params['RAW_RELATIONS'] ?? 'N') === 'N')
		{
			$relations = self::filterRelationsByAccess($chatId, $relations);
		}

		// region New counter
		// todo: select counter only if it's need
		if (!isset($params['WITHOUT_COUNTERS']) || $params['WITHOUT_COUNTERS'] !== 'Y')
		{

			$readService = new ReadService();
			$counters = $readService->getCounterService()->getByChatForEachUsers($chatId, $userIds);
			$lastIdInChat = $readService->getLastMessageIdInChat($chatId);
			$lastReads = $readService->getViewedService()->getDateViewedByMessageIdForEachUser($lastIdInChat, $userIds);
			foreach ($relations as $userId => $relation)
			{
				$counter = $counters[$userId] ?? 0;
				$counter = $counter > 99 ? 100 : $counter;
				$relations[$userId]['COUNTER'] = $counter;
				$relations[$userId]['LAST_READ'] = $lastReads[$userId] ?? null;
			}
		}
		// endregion

		return $relations;
	}

	public static function mute($chatId, $action, $userId = null)
	{
		$userId = \Bitrix\Im\Common::getUserId($userId);
		if (!$userId)
		{
			return false;
		}

		$chatId = intval($chatId);
		if (!$chatId)
		{
			return false;
		}

		$action = $action === true? 'Y': 'N';

		(new CounterService())->withContextUser($userId)->updateIsMuted($chatId, $action);

		$relation = self::getRelation($chatId, Array(
			'SELECT' => Array('ID', 'MESSAGE_TYPE', 'NOTIFY_BLOCK', 'COUNTER'),
			'FILTER' => Array(
				'USER_ID' => $userId
			),
		));
		if (!$relation)
		{
			return false;
		}

		if ($relation[$userId]['NOTIFY_BLOCK'] == $action)
		{
			return true;
		}

		\Bitrix\Im\Model\RelationTable::update($relation[$userId]['ID'], array('NOTIFY_BLOCK' => $action));

		Recent::clearCache($userId);
		$chat = \Bitrix\Im\Chat::getById($chatId);
		//Counter::clearCache($userId);

		if (\Bitrix\Main\Loader::includeModule('pull'))
		{
			$element = \Bitrix\Im\Model\RecentTable::getList([
				'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'UNREAD'],
				'filter' => [
					'=USER_ID' => $userId,
					'=ITEM_TYPE' => $relation[$userId]['MESSAGE_TYPE'],
					'=ITEM_ID' => $chatId
				]
			])->fetch();

			$counter = $relation[$userId]['COUNTER'];
			$chatObject = \Bitrix\Im\V2\Chat::getInstance($chatId);

			\Bitrix\Pull\Event::add($userId, Array(
				'module_id' => 'im',
				'command' => 'chatMuteNotify',
				'params' => Array(
					'chatId' => $chatId,
					'dialogId' => 'chat'.$chatId,
					'muted' => $action == 'Y',
					'mute' => $action == 'Y', // TODO remove this later
					'counter' => $counter,
					'lines' => $element['ITEM_TYPE'] === self::TYPE_OPEN_LINE,
					'unread' => ($element['UNREAD'] ?? 'N') === 'Y',
					'counterType' => $chatObject->getCounterType()->value,
					'recentConfig' => $chatObject->getRecentConfig()->toPullFormat(),
				),
				'extra' => \Bitrix\Im\Common::getPullExtra()
			));
		}

		foreach(\Bitrix\Main\EventManager::getInstance()->findEventHandlers("im", "OnAfterChatMuteNotify") as $event)
		{
			ExecuteModuleEventEx($event, [[
				'CHAT_ID' => $chatId,
				'USER_ID' => $userId,
				'MUTE' => $action == 'Y',
				'CHAT' => $chat,
			]]);
		}

		return true;
	}

	public static function getMessageCount($chatId, $userId = null)
	{
		$chatId = intval($chatId);
		if (!$chatId)
		{
			return false;
		}

		$userId = \Bitrix\Im\Common::getUserId($userId);
		if (!$userId)
		{
			return false;
		}

		$relationData = \Bitrix\Im\Model\RelationTable::getList(Array(
			'select' => Array('START_ID'),
			'filter' => Array('=CHAT_ID' => $chatId, '=USER_ID' => $userId)
		))->fetch();

		if (!$relationData || $relationData['START_ID'] == 0)
		{
			$counter = \Bitrix\Im\Model\MessageTable::getCount(['=CHAT_ID' => $chatId]);
		}
		else
		{
			$counter = \Bitrix\Im\Model\MessageTable::getCount(['=CHAT_ID' => $chatId, '>=ID' => $relationData['START_ID']]);
		}

		return $counter > 0 ? $counter : 0;
	}

	public static function hasAccess($chatId)
	{
		$chatId = intval($chatId);
		if (!$chatId)
		{
			return false;
		}

		return \Bitrix\Im\Dialog::hasAccess('chat'.$chatId);
	}

	/**
	 * @param $chatId
	 * @param null $userId
	 * @param array $options
	 * @return array|bool
	 */
	public static function getMessages($chatId, $userId = null, $options = Array())
	{
		$userId = \Bitrix\Im\Common::getUserId($userId);
		if (!$userId)
		{
			return false;
		}

		$chatData = \Bitrix\Im\Model\ChatTable::getList(Array(
			'select' => Array(
				'CHAT_ID' => 'ID',
				'CHAT_TYPE' => 'TYPE',
				'CHAT_ENTITY_TYPE' => 'ENTITY_TYPE',
				'CHAT_ENTITY_ID' => 'ENTITY_ID',
				'RELATION_USER_ID' => 'RELATION.USER_ID',
				'RELATION_START_ID' => 'RELATION.START_ID',
				//'RELATION_UNREAD_ID' => 'RELATION.UNREAD_ID',
				'RELATION_LAST_ID' => 'RELATION.LAST_ID',
				//'RELATION_STATUS' => 'RELATION.STATUS',
				//'RELATION_COUNTER' => 'RELATION.COUNTER'
			),
			'filter' => Array('=ID' => $chatId),
			'runtime' => Array(
				new \Bitrix\Main\Entity\ReferenceField(
					'RELATION',
					'\Bitrix\Im\Model\RelationTable',
					array(
						"=ref.CHAT_ID" => "this.ID",
						"=ref.USER_ID" => new \Bitrix\Main\DB\SqlExpression('?i', $userId)
					),
					array("join_type"=>"LEFT")
				)
			)
		))->fetch();
		if (!$chatData)
		{
			return false;
		}

		$readService = new ReadService($userId);

		$chatData['RELATION_UNREAD_ID'] = $readService->getCounterService()->getIdFirstUnreadMessage($chatId) ?? 0;
		$chatData['RELATION_COUNTER'] = $readService->getCounterService()->getByChat($chatId);
		$chatData['RELATION_START_ID'] = (int)$chatData['RELATION_START_ID'];

		if (isset($options['LIMIT']))
		{
			$options['LIMIT'] = intval($options['LIMIT']);
			$limit = $options['LIMIT'] >= 100? 100: $options['LIMIT'];
		}
		else
		{
			$limit = 50;
		}

		$filter = Array(
			'=CHAT_ID' => $chatId
		);

		$fileSort = 'ASC';
		$startFromUnread = false;
		if (
			!isset($options['LAST_ID'])
			&& !isset($options['FIRST_ID'])
			//&& $chatData['RELATION_STATUS'] != \Bitrix\Im\Chat::STATUS_READ
			&& $chatData['RELATION_COUNTER'] > 0
		)
		{
			if ($chatData['RELATION_COUNTER'] > $limit)
			{
				$startFromUnread = true;
				$options['FIRST_ID'] = $chatData['RELATION_LAST_ID'];
			}
			else
			{
				$limit += $chatData['RELATION_COUNTER'];
			}
		}

		if (isset($options['FIRST_ID']))
		{
			$orderId = [];
			$orderResult = [];

			if ($chatData['RELATION_START_ID'] > 0 && intval($options['FIRST_ID']) < $chatData['RELATION_START_ID'])
			{
				$filter['>=ID'] = $chatData['RELATION_START_ID'];
			}
			else
			{
				if (intval($options['FIRST_ID']) > 0)
				{
					$filter['>ID'] = $options['FIRST_ID'];
				}
			}
		}
		else
		{
			$fileSort = 'DESC';
			$orderId = Array('CHAT_ID' => 'ASC', 'ID' => 'DESC');
			$orderResult = Array('ID' => 'DESC');

			if ($chatData['RELATION_START_ID'] > 0)
			{
				$filter['>=ID'] = $chatData['RELATION_START_ID'];
			}

			if (isset($options['LAST_ID']) && intval($options['LAST_ID']) > 0)
			{
				$filter['<ID'] = intval($options['LAST_ID']);
			}
		}

		$orm = \Bitrix\Im\Model\MessageTable::getList(array(
			'select' => ['ID'],
			'filter' => $filter,
			'order' => $orderId,
			'limit' => $limit
		));
		$ids = array_map(fn ($item) => $item['ID'], $orm->fetchAll());
		if (empty($ids))
		{
			$result = [
				'CHAT_ID' => (int)$chatId,
				'MESSAGES' => [],
				'USERS' => [],
				'FILES' => [],
			];
			if ($options['JSON'])
			{
				$result = array_change_key_case($result, CASE_LOWER);
			}
			return $result;
		}

		$orm = \Bitrix\Im\Model\MessageTable::getList(array(
			'select' => [
				'ID', 'AUTHOR_ID', 'DATE_CREATE', 'NOTIFY_EVENT', 'MESSAGE',
				'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',*/
				'MESSAGE_UUID' => 'UUID.UUID',
			],
			'filter' => ['=ID' => $ids],
			'order' => $orderResult,
		));

		$users = Array();

		$userOptions = ['SKIP_ONLINE' => 'Y'];
		if ($options['JSON'] == 'Y')
		{
			$userOptions['JSON'] = 'Y';
		}
		if ($chatData['CHAT_ENTITY_TYPE'] == 'LIVECHAT')
		{
			[$lineId] = explode('|', $chatData['CHAT_ENTITY_ID']);
			$userOptions['LIVECHAT'] = $lineId;
			$userOptions['USER_CODE'] = 'livechat|' . $lineId . '|' . $chatData['CHAT_ID'] . '|' . $userId;
		}

		$messages = Array();
		while($message = $orm->fetch())
		{
			if ($message['NOTIFY_EVENT'] == 'private_system')
			{
				$message['AUTHOR_ID'] = 0;
			}

			if (isset($options['USER_TAG_SPREAD']) && $options['USER_TAG_SPREAD'] === 'Y')
			{
				$message['MESSAGE'] = preg_replace_callback("/\[USER=([0-9]{1,})\]\[\/USER\]/i", Array('\Bitrix\Im\Text', 'modifyShortUserTag'), $message['MESSAGE']);
			}

			$messages[$message['ID']] = Array(
				'ID' => (int)$message['ID'],
				'CHAT_ID' => (int)$chatId,
				'AUTHOR_ID' => (int)$message['AUTHOR_ID'],
				'DATE' => $message['DATE_CREATE'],
				'TEXT' => (string)\Bitrix\Im\Text::parse($message['MESSAGE']),
				'UNREAD' => $chatData['RELATION_USER_ID'] > 0 && $chatData['RELATION_LAST_ID'] < $message['ID'],
				'UUID' => $message['MESSAGE_UUID'],
			);
			$messages[$message['ID']]['REPLACES'] = \Bitrix\Im\Text::getReplaceMap($messages[$message['ID']]['TEXT']);
			if ($message['AUTHOR_ID'] && !isset($users[$message['AUTHOR_ID']]))
			{
				$user = User::getInstance($message['AUTHOR_ID'])->getArray($userOptions);
				$user['last_activity_date'] = $message['USER_LAST_ACTIVITY_DATE']? date('c', $message['USER_LAST_ACTIVITY_DATE']->getTimestamp()): false;
				$user['desktop_last_date'] = false;
				$user['mobile_last_date'] = false;
				$user['idle'] = false;

				$users[$message['AUTHOR_ID']] = $user;
			}
			if ($options['CONVERT_TEXT'])
			{
				$messages[$message['ID']]['TEXT_CONVERTED'] = \Bitrix\Im\Text::parseLegacyFormat($message['MESSAGE']);
			}
		}

		$params = \CIMMessageParam::Get(array_keys($messages));

		$fileIds = Array();
		foreach ($params as $messageId => $param)
		{
			$messages[$messageId]['PARAMS'] = empty($param)? []: $param;

			if (
				empty($messages[$messageId]['TEXT'])
				&& !isset($param['FILE_ID'])
				&& !isset($param['KEYBOARD'])
				&& !isset($param['ATTACH'])
			)
			{
				$messages[$messageId]['TEXT'] = Loc::getMessage('IM_CHAT_MESSAGE_DELETED');
				$messages[$messageId]['PARAMS']['IS_DELETED'] = 'Y';
			}

			if (isset($param['FILE_ID']))
			{
				foreach ($param['FILE_ID'] as $fileId)
				{
					$fileIds[$fileId] = $fileId;
				}
			}

			if (isset($param['CHAT_USER']) && is_array($param['CHAT_USER']))
			{
				foreach ($param['CHAT_USER'] as $paramsUserId)
				{
					$users[$paramsUserId] = User::getInstance($paramsUserId)->getArray($userOptions);
				}
			}
		}

		$disappearing = DisappearService::getMessagesDisappearingTime(array_keys($messages));
		foreach ($messages as $messageId => $message)
		{
			if (
				isset($disappearing[$messageId])
				&& $disappearing[$messageId]['DATE_REMOVE'] instanceof DateTime
			)
			{
				$messages[$messageId]['DISAPPEARING_DATE'] = $disappearing[$messageId]['DATE_REMOVE']->format(DATE_ATOM);
			}
			else
			{
				$messages[$messageId]['DISAPPEARING_DATE'] = null;
			}
		}

		$messages = \CIMMessageLink::prepareShow($messages, $params);

		$files = \CIMDisk::GetFiles($chatId, $fileIds);

		$result = Array(
			'CHAT_ID' => (int)$chatId,
			'MESSAGES' => $messages,
			'USERS' => array_values($users),
			'FILES' => array_values($files),
		);

		if (count($files) && $fileSort == 'DESC')
		{
			$result['FILES'] = array_reverse($result['FILES']);
		}

		if ($startFromUnread)
		{
			$result['MESSAGES'] = array_reverse($result['MESSAGES']);
			$additionalMessages = self::getMessages($chatId, $userId, [
				'LIMIT' => $limit,
				'LAST_ID' => $chatData['RELATION_UNREAD_ID']
			]);
			$result['MESSAGES'] = array_merge($result['MESSAGES'], $additionalMessages['MESSAGES']);
		}

		if ($options['JSON'])
		{
			foreach ($result['MESSAGES'] as $key => $value)
			{
				if ($value['DATE'] instanceof \Bitrix\Main\Type\DateTime)
				{
					$result['MESSAGES'][$key]['DATE'] = date('c', $value['DATE']->getTimestamp());
				}

				if (isset($value['PARAMS']['CHAT_LAST_DATE']) && $value['PARAMS']['CHAT_LAST_DATE'] instanceof \Bitrix\Main\Type\DateTime)
				{
					$result['MESSAGES'][$key]['PARAMS']['CHAT_LAST_DATE'] = date('c', $value['PARAMS']['CHAT_LAST_DATE']->getTimestamp());
				}

				if (is_array($value['REPLACES']) && !empty($value['REPLACES']))
				{
					$result['MESSAGES'][$key]['REPLACES'] = Common::toJson($value['REPLACES']);
				}

				$result['MESSAGES'][$key] = array_change_key_case($result['MESSAGES'][$key], CASE_LOWER);
			}
			$result['MESSAGES'] = array_values($result['MESSAGES']);

			foreach ($result['FILES'] as $key => $value)
			{
				if ($value['date'] instanceof \Bitrix\Main\Type\DateTime)
				{
					$result['FILES'][$key]['date'] = date('c', $value['date']->getTimestamp());
				}

				foreach (['urlPreview', 'urlShow', 'urlDownload'] as $field)
				{
					$url = $result['FILES'][$key][$field];
					if (is_string($url) && $url && mb_strpos($url, 'http') !== 0)
					{
						$result['FILES'][$key][$field] = \Bitrix\Im\Common::getPublicDomain().$url;
					}
				}

			}

			$result = array_change_key_case($result, CASE_LOWER);
		}

		return $result;
	}

	public static function getUsers($chatId, $options = []): array
	{
		$params = [
			'SELECT' => ['ID', 'USER_ID'],
			'SKIP_INACTIVE_USER' => 'Y',
			'WITHOUT_COUNTERS' => 'Y',
		];

		$skipExternal = isset($options['SKIP_EXTERNAL']) || isset($options['SKIP_EXTERNAL_EXCEPT_TYPES']);
		if ($skipExternal)
		{
			$exceptType = $options['SKIP_EXTERNAL_EXCEPT_TYPES'] ?? [];
			$params['SKIP_USER_TYPES'] = \Bitrix\Im\Model\UserTable::filterExternalUserTypes($exceptType);
		}

		if (isset($options['LIMIT']))
		{
			$params['LIMIT'] = $options['LIMIT'];
		}
		if (isset($options['OFFSET']))
		{
			$params['OFFSET'] = $options['OFFSET'];
		}
		if (isset($options['LAST_ID']))
		{
			$params['LAST_USER_ID'] = (int)$options['LAST_ID'];
		}

		$json = isset($options['JSON']) && $options['JSON'] === 'Y' ? 'Y' : 'N';
		$users = [];
		$relations = self::getRelation($chatId, $params);
		foreach ($relations as $user)
		{
			$userData = \Bitrix\Im\User::getInstance($user['USER_ID'])->getArray([
				'JSON' => $json
			]);

			if ($userData['bot'])
			{
				$converter = new Converter(Converter::TO_SNAKE | Converter::TO_LOWER | Converter::KEYS);

				$botData = \Bitrix\Im\V2\Entity\User\Data\BotData::getInstance((int)$user['USER_ID'])->toRestFormat();
				$userData['bot_data'] = (!empty($botData)) ? $converter->process($botData) : null;
			}
			else
			{
				$userData['bot_data'] = null;
			}

			$users[] = $userData;
		}

		return $users;
	}

	/**
	 * @param $id
	 * @param array $params
	 * @return array|bool|mixed
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Bitrix\Main\LoaderException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 */
	public static function getById($id, $params = array())
	{
		$userId = \Bitrix\Im\Common::getUserId($params['USER_ID'] ?? null);
		if (!$userId)
		{
			return false;
		}

		$checkAccessParam = $params['CHECK_ACCESS'] ?? null;
		$chats = self::getList(Array(
			'FILTER' => Array('ID' => $id),
			'SKIP_ACCESS_CHECK' => $checkAccessParam === 'Y'? 'N': 'Y',
			'CURRENT_USER' => $userId,
 		));
		if ($chats)
		{
			$chat = $chats[0];
		}
		else
		{
			return false;
		}

		if (isset($params['LOAD_READED']) && $params['LOAD_READED'])
		{
			$userOptions = ['SKIP_ONLINE' => 'Y'];
			if ($chat['ENTITY_TYPE'] == 'LIVECHAT')
			{
				[$lineId] = explode('|', $chat['CHAT_ENTITY_ID']);
				$userOptions['LIVECHAT'] = $lineId;
				$userOptions['USER_CODE'] = 'livechat|' . $lineId . '|' . $id . '|' . $userId;
			}

			$relations = self::getRelation($id);

			$chat['READED_LIST'] = [];
			$chat['MANAGER_LIST'] = [];
			foreach ($relations as $relation)
			{
				if (
					$relation['USER_ID'] != $userId
					//&& $relation['STATUS'] == self::STATUS_READ
					&& \Bitrix\Im\User::getInstance($relation['USER_ID'])->isActive()
				)
				{
					$user = \Bitrix\Im\User::getInstance($relation['USER_ID'])->getArray($userOptions);
					$chat['READED_LIST'][] = [
						'USER_ID' => (int)$relation['USER_ID'],
						'USER_NAME' => $user['NAME'],
						'MESSAGE_ID' => (int)$relation['LAST_ID'],
						'DATE' => $relation['LAST_READ'],
					];
				}

				if ($relation['MANAGER'] === 'Y')
				{
					$chat['MANAGER_LIST'][] = (int)$relation['USER_ID'];
				}
			}

			// region v2

			$chatInstance = \Bitrix\Im\V2\Chat::getInstance((int)$id);
			$chat['LAST_MESSAGE_VIEWS'] = $chatInstance->getLastMessageViews();

			// endregion
		}

		if ($params['JSON'] ?? null)
		{
			$chat = self::toJson($chat);
		}

		return $chat;
	}

	public static function getList($params = array())
	{
		$params = is_array($params)? $params: Array();

		if (!isset($params['CURRENT_USER']) && is_object($GLOBALS['USER']))
		{
			$params['CURRENT_USER'] = $GLOBALS['USER']->GetID();
		}

		$params['CURRENT_USER'] = intval($params['CURRENT_USER']);

		$params['SKIP_ACCESS_CHECK'] = $params['SKIP_ACCESS_CHECK'] === 'Y'? 'Y': 'N';

		$userId = $params['CURRENT_USER'];
		if ($userId <= 0)
		{
			return false;
		}

		$enableLimit = false;
		if (isset($params['OFFSET']))
		{
			$filterLimit = intval($params['LIMIT']);
			$filterLimit = $filterLimit <= 0? self::FILTER_LIMIT: $filterLimit;

			$filterOffset = intval($params['OFFSET']);

			$enableLimit = true;
		}
		else
		{
			$filterLimit = false;
			$filterOffset = false;
		}

		$ormParams = self::getListParams($params);
		if (!$ormParams)
		{
			return false;
		}
		if ($enableLimit)
		{
			$ormParams['offset'] = $filterOffset;
			$ormParams['limit'] = $filterLimit;
		}
		if (isset($params['ORDER']))
		{
			$ormParams['order'] = $params['ORDER'];
		}

		$orm = \Bitrix\Im\Model\ChatTable::getList($ormParams);
		$chatsRaw = $orm->fetchAll();
		$chatsRaw = self::fillCounterData($chatsRaw);

		$chats = array();
		foreach ($chatsRaw as $chatRaw)
		{
			$chats[] = self::formatChatData($chatRaw);
		}

		if (isset($params['JSON']) && $params['JSON'])
		{
			$chats = self::toJson($chats);
		}

		return $chats;
	}

	public static function formatChatData($chat): array
	{
		$generalChatId = \CIMChat::GetGeneralChatId();
		$avatar = \CIMChat::GetAvatarImage($chat['AVATAR'], 200, false);
		$color = $chat['COLOR'] <> ''? Color::getColor($chat['COLOR']): Color::getColorByNumber($chat['ID']);

		if ($generalChatId == $chat['ID'])
		{
			$chat["ENTITY_TYPE"] = 'GENERAL';
		}

		$chatType = \Bitrix\Im\Chat::getType($chat);

		$muteList = Array();
		if ($chat['RELATION_NOTIFY_BLOCK'] == 'Y')
		{
			$muteList[] = (int)$chat['RELATION_USER_ID'];
		}

		$counter = (int)$chat['RELATION_COUNTER'];
		$startCounter = (int)$chat['RELATION_START_COUNTER'];
		$userCounter = (int)$chat['USER_COUNT'];
		$unreadId = (int)$chat['RELATION_UNREAD_ID'];
		$lastMessageId = (int)$chat['LAST_MESSAGE_ID'];

		$publicOption = '';
		if ($chat['ALIAS_NAME'])
		{
			$publicOption = [
				'code' => $chat['ALIAS_NAME'],
				'link' => Alias::getPublicLink($chat['ENTITY_TYPE'], $chat['ALIAS_NAME'])
			];
		}

		$options = \CIMChat::GetChatOptions();
		$restrictions = $options['DEFAULT'];

		if ($chat["ENTITY_TYPE"] && in_array($chat["ENTITY_TYPE"], array_keys($options), true))
		{
			$restrictions = $options[$chat['ENTITY_TYPE']];
		}

		return Array(
			'ID' => (int)$chat['ID'],
			'PARENT_CHAT_ID' => (int)$chat['PARENT_ID'],
			'PARENT_MESSAGE_ID' => (int)$chat['PARENT_MID'],
			'NAME' => $chat['TITLE'],
			'DESCRIPTION' => $chat['DESCRIPTION'],
			'OWNER' => (int)$chat['AUTHOR_ID'],
			'EXTRANET' => $chat['EXTRANET'] == 'Y',
			'AVATAR' => $avatar,
			'COLOR' => $color,
			'TYPE' => $chatType,
			'COUNTER' => $counter,
			'USER_COUNTER' => $userCounter,
			'MESSAGE_COUNT' => (int)$chat['MESSAGE_COUNT'] - $startCounter,
			'UNREAD_ID' => $unreadId,
			'RESTRICTIONS' => $restrictions,
			'LAST_MESSAGE_ID' => $lastMessageId,
			'LAST_ID' => (int)$chat['RELATION_LAST_ID'],
			'MARKED_ID' => (int)$chat['MARKED_ID'],
			'DISK_FOLDER_ID' => (int)$chat['DISK_FOLDER_ID'],
			'ENTITY_TYPE' => (string)$chat['ENTITY_TYPE'],
			'ENTITY_ID' => (string)$chat['ENTITY_ID'],
			'ENTITY_DATA_1' => (string)$chat['ENTITY_DATA_1'],
			'ENTITY_DATA_2' => (string)$chat['ENTITY_DATA_2'],
			'ENTITY_DATA_3' => (string)$chat['ENTITY_DATA_3'],
			'MUTE_LIST' => $muteList,
			'DATE_CREATE' => $chat['DATE_CREATE'],
			'MESSAGE_TYPE' => $chat["TYPE"],
			'PUBLIC' => $publicOption,
			'ROLE' => mb_strtolower(self::getRole($chat)),
			'ENTITY_LINK' => EntityLink::getInstance(\CIMChat::initChatByArray($chat))->toArray(),
			'TEXT_FIELD_ENABLED' => (new TextFieldEnabled((int)$chat['ID']))->get(),
			'BACKGROUND_ID' => (new Background((int)$chat['ID']))->get(),
			'PERMISSIONS' => [
				'MANAGE_USERS_ADD' => mb_strtolower((string)$chat['MANAGE_USERS_ADD']),
				'MANAGE_USERS_DELETE' => mb_strtolower((string)$chat['MANAGE_USERS_DELETE']),
				'MANAGE_UI' => mb_strtolower((string)$chat['MANAGE_UI']),
				'MANAGE_SETTINGS' => mb_strtolower((string)$chat['MANAGE_SETTINGS']),
				'MANAGE_MESSAGES' => mb_strtolower((string)$chat['CAN_POST']),
				'CAN_POST' => mb_strtolower((string)$chat['CAN_POST']),
			],
			'IS_NEW' => \CIMChat::isNewChat($chat['TYPE'], $chat['DATE_CREATE']),
		);
	}

	public static function getListParams($params)
	{
		if (!isset($params['CURRENT_USER']) && is_object($GLOBALS['USER']))
		{
			$params['CURRENT_USER'] = $GLOBALS['USER']->GetID();
		}

		$params['CURRENT_USER'] = intval($params['CURRENT_USER']);

		$userId = $params['CURRENT_USER'];
		if ($userId <= 0)
		{
			return null;
		}

		$filter = [];
		$runtime = [];

		$find = null;
		$field = '*INDEX.SEARCH_CONTENT';

		if (isset($params['FILTER']['SEARCH']))
		{
			$find = (string)$params['FILTER']['SEARCH'];
		}
		elseif (isset($params['FILTER']['SEARCH_OL']) && Loader::includeModule('imopenlines'))
		{
			$find = (string)$params['FILTER']['SEARCH_OL'];
			$field = '*OL_INDEX.SEARCH_TITLE';
		}

		if (isset($params['FILTER']['ID']))
		{
			$filter['=ID'] = $params['FILTER']['ID'];
		}
		else if (isset($find))
		{
			$helper = Application::getConnection()->getSqlHelper();
			if (Model\ChatIndexTable::getEntity()->fullTextIndexEnabled('SEARCH_CONTENT'))
			{
				$find = trim($find);
				$find = \Bitrix\Main\Search\Content::prepareStringToken($find);

				if (\Bitrix\Main\Search\Content::canUseFulltextSearch($find, \Bitrix\Main\Search\Content::TYPE_MIXED))
				{
					$filter[$field] = $find;
				}
				else
				{
					return null;
				}
			}
			else
			{
				if (mb_strlen($find) < 3)
				{
					return null;
				}

				$filter['%=INDEX.SEARCH_TITLE'] = $helper->forSql($find).'%';
			}
		}

		if ($params['SKIP_ACCESS_CHECK'] === 'Y')
		{
			// do nothing
		}
		else if (
			User::getInstance($params['CURRENT_USER'])->isExtranet()
			|| User::getInstance($params['CURRENT_USER'])->isBot()
		)
		{
			$filter['=TYPE'] = [
				self::TYPE_CHANNEL,
				self::TYPE_GROUP,
				self::TYPE_THREAD,
				self::TYPE_PRIVATE
			];
			if (User::getInstance($params['CURRENT_USER'])->isBot() && Loader::includeModule('imopenlines'))
			{
				$filter['=TYPE'][] = self::TYPE_OPEN_LINE;
				$filter[] = [
					'LOGIC' => 'OR',
					[
						'=RELATION.USER_ID' => $params['CURRENT_USER']
					],
					[
						'=RECENT_OL.USER_ID' => $params['CURRENT_USER']
					]
				];
			}
			else
			{
				$filter['=RELATION.USER_ID'] = $params['CURRENT_USER'];
			}
		}
		else
		{
			$condition = [
				'LOGIC' => 'OR',
				[
					'=TYPE' => self::TYPE_OPEN,
				],
				[
					'=TYPE' => self::TYPE_GROUP,
					'=RELATION.USER_ID' => $params['CURRENT_USER']
				],
				[
					'=TYPE' => self::TYPE_THREAD,
					'=RELATION.USER_ID' => $params['CURRENT_USER']
				],
				[
					'=TYPE' => self::TYPE_PRIVATE,
					'=RELATION.USER_ID' => $params['CURRENT_USER']
				],
				[
					'=TYPE' => self::TYPE_OPEN_LINE,
					'=RELATION.USER_ID' => $params['CURRENT_USER']
				],
			];
			if (Loader::includeModule('imopenlines'))
			{
				$condition[] = [
					'=TYPE' => self::TYPE_OPEN_LINE,
					'=RECENT_OL.USER_ID' => $params['CURRENT_USER']
				];
			}
			$filter[] = $condition;
		}

		$runtime[] = new \Bitrix\Main\Entity\ReferenceField(
			'RELATION',
			'Bitrix\Im\Model\RelationTable',
			array(
				"=ref.CHAT_ID" => "this.ID",
				"=ref.USER_ID" => new \Bitrix\Main\DB\SqlExpression('?i', $params['CURRENT_USER']),
			),
			array("join_type"=>"LEFT")
		);
		if (Loader::includeModule('imopenlines'))
		{
			$runtime[] = new \Bitrix\Main\Entity\ReferenceField(
				'RECENT_OL',
				\Bitrix\ImOpenLines\Model\RecentTable::class,
				array(
					"=ref.CHAT_ID" => "this.ID",
					"=ref.USER_ID" => new \Bitrix\Main\DB\SqlExpression('?i', $params['CURRENT_USER']),
				),
				array("join_type"=>"LEFT")
			);
		}

		return [
			'select' => [
				'*',
				'RELATION_USER_ID' => 'RELATION.USER_ID',
				'RELATION_MANAGER' => 'RELATION.MANAGER',
				'RELATION_NOTIFY_BLOCK' => 'RELATION.NOTIFY_BLOCK',
				//'RELATION_COUNTER' => 'RELATION.COUNTER',
				'RELATION_START_COUNTER' => 'RELATION.START_COUNTER',
				'RELATION_LAST_ID' => 'RELATION.LAST_ID',
				//'RELATION_STATUS' => 'RELATION.STATUS',
				//'RELATION_UNREAD_ID' => 'RELATION.UNREAD_ID',
				'ALIAS_NAME' => 'ALIAS.ALIAS',
			],
			'filter' => $filter,
			'runtime' => $runtime
		];
	}

	public static function toJson($array)
	{
		return \Bitrix\Im\Common::toJson($array, false);
	}

	public static function isUserInChat($chatId, $userId = 0) : bool
	{
		if ($userId === 0)
		{
			$userId = \Bitrix\Im\Common::getUserId();
		}

		if (!$userId)
		{
			return false;
		}

		$result = \Bitrix\Im\Model\RelationTable::getList(
			[
				'select' => ["ID"],
				'filter' => [
					'=USER_ID' => $userId,
					'=CHAT_ID' => $chatId
				]
			]
		)->fetch();

		return (bool)$result['ID'];
	}

	public static function isUserKickedFromChat($chatId, $userId = 0) : bool
	{
		if ($userId === 0)
		{
			$userId = \Bitrix\Im\Common::getUserId();
		}

		if (!$userId)
		{
			return false;
		}

		$result = BlockUserTable::getList(
			[
				'select' => ["ID"],
				'filter' => [
					'=USER_ID' => $userId,
					'=CHAT_ID' => $chatId
				]
			]
		)->fetch();

		return is_array($result) && (bool)$result['ID'];
	}

	public static function checkReplicaDeprecatedAgent(): string
	{
		return '';
	}

	/**
	 * Returns the value of the chat option by dialogId.
	 *
	 * @param int|string $dialogId
	 *
	 * @param string $action - chat option.
	 * @see \CIMChat::GetChatOptions()
	 *
	 * @param string|null $entityType - if $entityType is known, you can avoid accessing the database.
	 *
	 * @return bool
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 */
	public static function isActionAllowed($dialogId, $action, $entityType = null): bool
	{
		if (!\Bitrix\Im\Common::isChatId($dialogId))
		{
			return true;
		}

		$chatOptions = \CIMChat::GetChatOptions();
		$isAllowedByDefault = (bool)($chatOptions['DEFAULT'][$action] ?? true);

		if ($entityType && $chatOptions[$entityType])
		{
			return (bool)($chatOptions[$entityType][$action] ?? $isAllowedByDefault);
		}

		if ($entityType)
		{
			return $isAllowedByDefault;
		}

		$chatId = \Bitrix\Im\Dialog::getChatId($dialogId);
		if (!$chatId)
		{
			return $isAllowedByDefault;
		}

		$generalChatId = (int)\CIMChat::GetGeneralChatId();
		if ($chatId === $generalChatId)
		{
			return (bool)($chatOptions['GENERAL'][$action] ?? $isAllowedByDefault);
		}

		$chat = \Bitrix\Im\Model\ChatTable::getList([
			'select' => [
				'ID',
				'ENTITY_TYPE',
			],
			'filter' => [
				'ID' => $chatId,
			]
		])->fetch();

		$entityType = ($chat && $chat['ENTITY_TYPE']) ? $chat['ENTITY_TYPE'] : null;

		if ($entityType && $chatOptions[$entityType])
		{
			return (bool)($chatOptions[$entityType][$action] ?? $isAllowedByDefault);
		}

		return $isAllowedByDefault;
	}

	/**
	 * Get chat authorId by dialogId
	 *
	 * @param int|string $dialogId
	 *
	 * @return int|null AUTHOR_ID
	 *
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 */
	public static function getOwnerById($dialogId): ?int
	{
		$chatId = \Bitrix\Im\Dialog::getChatId($dialogId);
		if (!$chatId)
		{
			return null;
		}

		$chat = \Bitrix\Im\Model\ChatTable::getList([
			'select' => [
				'ID',
				'AUTHOR_ID',
			],
			'filter' => [
				'ID' => $chatId,
			]
		])->fetch();

		return ($chat && is_numeric($chat['AUTHOR_ID'])) ? (int)$chat['AUTHOR_ID'] : null;
	}

	public static function fillCounterData(array $chats): array
	{
		if (empty($chats))
		{
			return [];
		}

		$userId = \Bitrix\Im\Common::getUserId();
		$readService = new ReadService($userId);

		$chatIds = [];

		foreach ($chats as $chat)
		{
			$chatIds[] = (int)$chat['ID'];
		}

		$counters = $readService->getCounterService()->getForEachChat($chatIds);
		$unreadIds = $readService->getCounterService()->getIdFirstUnreadMessageForEachChats($chatIds);
		$markedIds = Recent::getMarkedIdByChatIds($userId, $chatIds);

		foreach ($chats as $key => $chat)
		{
			$id = (int)$chat['ID'];
			$chats[$key]['RELATION_COUNTER'] = $counters[$id] ?? 0;
			$chats[$key]['RELATION_UNREAD_ID'] = $unreadIds[$id] ?? 0;
			$chats[$key]['MARKED_ID'] = $markedIds[$id] ?? 0;
		}

		return $chats;
	}

	public static function filterRelationsByAccess(int $chatId, array $relations): array
	{
		$userIds = array_keys($relations);
		$usersWithAccess = \Bitrix\Im\V2\Chat::getInstance($chatId)
			->getRelationFacade()
			?->filterUserIdsByAccess($userIds)
			?? []
		;

		return array_filter(
			$relations,
			static fn ($userId) => in_array($userId, $usersWithAccess, true),
			ARRAY_FILTER_USE_KEY
		);
	}

	private static function getRole(array $chat): string
	{
		if (!isset($chat['RELATION_USER_ID']))
		{
			return \Bitrix\Im\V2\Chat::ROLE_GUEST;
		}
		if ((int)$chat['RELATION_USER_ID'] === (int)$chat['AUTHOR_ID'])
		{
			return \Bitrix\Im\V2\Chat::ROLE_OWNER;
		}
		if ($chat['RELATION_MANAGER'] === 'Y')
		{
			return \Bitrix\Im\V2\Chat::ROLE_MANAGER;
		}

		return \Bitrix\Im\V2\Chat::ROLE_MEMBER;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit