1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
ChatFactory.php
См. документацию.
1<?php
2
3namespace Bitrix\Im\V2\Chat;
4
5use Bitrix\Im\Model\ChatTable;
6use Bitrix\Im\V2\Analytics\ChatAnalytics;
7use Bitrix\Im\V2\Chat;
8use Bitrix\Im\V2\Chat\Ai\AiAssistantChat;
9use Bitrix\Im\V2\Chat\Ai\AiAssistantEntityChat;
10use Bitrix\Im\V2\Chat\Ai\AiAssistantPrivateChat;
11use Bitrix\Im\V2\Common\ContextCustomer;
12use Bitrix\Im\V2\Integration\AiAssistant\AiAssistantService;
13use Bitrix\Im\V2\Result;
14use Bitrix\Main\Application;
15use Bitrix\Main\Data\Cache;
16use Bitrix\Main\DI\ServiceLocator;
17
19{
20 use ContextCustomer;
21
22 public const NON_CACHED_FIELDS = ['MESSAGE_COUNT', 'USER_COUNT', 'LAST_MESSAGE_ID'];
23 private const LOCK_TIMEOUT = 3;
24
25 protected static self $instance;
27
28 private function __construct()
29 {
30 $this->aiAssistantService = ServiceLocator::getInstance()->get(AiAssistantService::class);
31 }
32
37 public static function getInstance(): self
38 {
39 if (isset(self::$instance))
40 {
41 return self::$instance;
42 }
43
44 self::$instance = new static();
45
46 return self::$instance;
47 }
48
49
50
51 //region Chat actions
52
57 public function getChat($params): ?Chat
58 {
59 $type = $params['TYPE'] ?? $params['MESSAGE_TYPE'] ?? '';
60
61 if (empty($params))
62 {
63 return null;
64 }
65 if (is_numeric($params))
66 {
67 $params = ['CHAT_ID' => (int)$params];
68 }
69 elseif (is_string($params))
70 {
71 $params = ['DIALOG_ID' => $params];
72 if (\Bitrix\Im\Common::isChatId($params['DIALOG_ID']))
73 {
74 $params['CHAT_ID'] = \Bitrix\Im\Dialog::getChatId($params['DIALOG_ID']);
75 }
76 }
77
78 $findResult = $this->findChat($params);
79
80 if ($findResult->hasResult())
81 {
82 $chatParams = $findResult->getResult();
83
84 return $this->initChat($chatParams);
85 }
86
87 if (
88 $type === Chat::IM_TYPE_SYSTEM
89 || $type === Chat::IM_TYPE_PRIVATE
90 )
91 {
92 $addResult = $this->addChat($params);
93 if ($addResult->hasResult())
94 {
95 $chat = $addResult->getResult()['CHAT'];
96 $chat->setContext($this->context);
97
98 return $chat;
99 }
100 }
101
102 return null;
103 }
104
108 public function getNotifyFeed($userId = null): ?NotifyChat
109 {
110 if (!$userId)
111 {
112 $userId = $this->getContext()->getUserId();
113 }
114
115 $params = [
116 'TYPE' => Chat::IM_TYPE_SYSTEM,
117 'TO_USER_ID' => $userId,
118 ];
119
120 return $this->getChat($params);
121 }
122
123 public function getEntityChat(string $entityType, string $entityId): Chat
124 {
125 $chatId = $this->getEntityChatId($entityType, $entityId);
126
127 return Chat::getInstance($chatId);
128 }
129
135 public function getGeneralChat(): ?GeneralChat
136 {
137 return GeneralChat::get();
138 }
139
140 public function getGeneralChannel(): ?ChannelChat
141 {
142 return GeneralChannel::get();
143 }
144
148 public function getPrivateChat($fromUserId, $toUserId): ?Chat\PrivateChat
149 {
150 $params = [
151 'TYPE' => Chat::IM_TYPE_PRIVATE,
152 'FROM_USER_ID' => $fromUserId,
153 'TO_USER_ID' => $toUserId,
154 ];
155
156 return $this->getChat($params);
157 }
158
162 public function getPersonalChat($userId = null): ?Chat\FavoriteChat
163 {
164 if (!$userId)
165 {
166 $userId = $this->getContext()->getUserId();
167 }
168
169 $params = [
170 'TYPE' => Chat::IM_TYPE_PRIVATE,
171 'FROM_USER_ID' => $userId,
172 'TO_USER_ID' => $userId,
173 ];
174
175 return $this->getChat($params);
176 }
177 //endregion
178
179 //region Chat Create
184 public function initChat(?array $params = null): Chat
185 {
186 $params['TYPE'] ??= $params['MESSAGE_TYPE'] ?? '';
187 $params['ENTITY_TYPE'] ??= '';
188
189 $type = $params['TYPE'];
190 $entityType = $params['ENTITY_TYPE'];
191
192 $chat = match (true)
193 {
194 $entityType === Chat::ENTITY_TYPE_FAVORITE || $entityType === 'PERSONAL' => new FavoriteChat($params),
195 $entityType === Chat::ENTITY_TYPE_GENERAL => new GeneralChat($params),
196 $entityType === Chat::ENTITY_TYPE_GENERAL_CHANNEL => new GeneralChannel($params),
197 $entityType === Chat::ENTITY_TYPE_LINE || $type === Chat::IM_TYPE_OPEN_LINE => new OpenLineChat($params),
199 $entityType === Chat::ENTITY_TYPE_VIDEOCONF => new VideoConfChat($params),
201 $type === Chat::IM_TYPE_CHANNEL => new ChannelChat($params),
202 $type === Chat::IM_TYPE_OPEN_CHANNEL => new OpenChannelChat($params),
203 $type === Chat::IM_TYPE_OPEN => new OpenChat($params),
204 $type === Chat::IM_TYPE_SYSTEM => new NotifyChat($params),
205 $type === Chat::IM_TYPE_PRIVATE => new PrivateChat($params),
206 $type === Chat::IM_TYPE_CHAT => new GroupChat($params),
207 $type === Chat::IM_TYPE_COMMENT => new CommentChat($params),
208 $type === Chat::IM_TYPE_COPILOT => new CopilotChat($params),
209 $type === Chat::IM_TYPE_COLLAB => new CollabChat($params),
210 $type === Chat::IM_TYPE_EXTERNAL => new ExternalChat($params),
211 $type === Chat::IM_TYPE_AI_ASSISTANT => new AiAssistantChat($params),
212 $type === Chat::IM_TYPE_AI_ASSISTANT_ENTITY => new AiAssistantEntityChat($params),
213 default => new NullChat(),
214 };
215
216 $chat->setContext($this->context);
217
218 return $chat;
219 }
220
221 protected function isPrivateAiAssistantChat(array $params): bool
222 {
223 $botId = $this->aiAssistantService->getBotId();
224
225 if ($params['TYPE'] !== Chat::IM_TYPE_PRIVATE || !$botId)
226 {
227 return false;
228 }
229
230 $users = [(int)($params['FROM_USER_ID'] ?? 0), (int)($params['TO_USER_ID'] ?? 0)];
231
232 return
233 $params['ENTITY_TYPE'] === Chat::ENTITY_TYPE_PRIVATE_AI_ASSISTANT
234 || in_array($botId, $users, true)
235 ;
236 }
237
242 public function createNotifyFeed(?array $params = null): Chat
243 {
244 $params = $params ?? [];
245 $params['TYPE'] = Chat::IM_TYPE_SYSTEM;
246
247 return $this->initChat($params);
248 }
249
254 public function createPersonalChat(?array $params = null): Chat
255 {
256 $params = $params ?? [];
257 $params['ENTITY_TYPE'] = Chat::ENTITY_TYPE_FAVORITE;
258
259 return $this->initChat($params);
260 }
261
266 public function createPrivateChat(?array $params = null): Chat
267 {
268 $params = $params ?? [];
269 $params['TYPE'] = Chat::IM_TYPE_PRIVATE;
270
271 return $this->initChat($params);
272 }
273
278 public function createOpenChat(?array $params = null): Chat
279 {
280 $params = $params ?? [];
281 $params['TYPE'] = Chat::IM_TYPE_OPEN;
282
283 return $this->initChat($params);
284 }
285
290 public function createOpenLineChat(?array $params = null): Chat
291 {
292 $params = $params ?? [];
293 $params['TYPE'] = Chat::IM_TYPE_OPEN_LINE;
294
295 return $this->initChat($params);
296 }
297
298 //endregion
299
300 //region Chat Find
301
302
307 public function getChatById(int $chatId): Chat
308 {
309 $findResult = $this->findChat(['CHAT_ID' => $chatId]);
310 if ($findResult->hasResult())
311 {
312 $chatParams = $findResult->getResult();
313
315 $chat = $this->initChat($chatParams);
316 }
317 else
318 {
319 $chat = new NullChat();
320 }
321
322 return $chat;
323 }
324
325
350 public function findChat(array $params): Result
351 {
352 $result = new Result;
353
354 if (isset($params['TYPE']))
355 {
356 $params['MESSAGE_TYPE'] = $params['TYPE'];
357 }
358
359 if (empty($params['CHAT_ID']) && !empty($params['DIALOG_ID']))
360 {
361 if (\Bitrix\Im\Common::isChatId($params['DIALOG_ID']))
362 {
363 $params['CHAT_ID'] = \Bitrix\Im\Dialog::getChatId($params['DIALOG_ID']);
364 if (!isset($params['MESSAGE_TYPE']))
365 {
366 $params['MESSAGE_TYPE'] = Chat::IM_TYPE_CHAT;
367 }
368 }
369 else
370 {
371 $params['TO_USER_ID'] = (int)$params['DIALOG_ID'];
372 $params['MESSAGE_TYPE'] = Chat::IM_TYPE_PRIVATE;
373 }
374 }
375
376 if (!empty($params['CHAT_ID']) && (int)$params['CHAT_ID'] > 0)
377 {
378 $chatId = (int)$params['CHAT_ID'];
379 $cache = $this->getCache($chatId);
380 $cachedChat = $cache->getVars();
381
382 if ($cachedChat !== false)
383 {
384 return $result->setResult($this->filterNonCachedFields($cachedChat));
385 }
386
387 $chat = $this->getRawById($chatId);
388
389 if ($chat)
390 {
391 $cache->startDataCache();
392 $cache->endDataCache($chat);
393 }
394 else
395 {
396 $chat = null;
397 }
398
399 return $result->setResult($chat);
400 }
401
402 switch ($params['MESSAGE_TYPE'] ?? '')
403 {
404 case Chat::IM_TYPE_SYSTEM:
405 $result = NotifyChat::find($params, $this->context);
406 break;
407
408 case Chat::IM_TYPE_PRIVATE:
409 if (
410 isset($params['TO_USER_ID'], $params['FROM_USER_ID'])
411 && $params['TO_USER_ID'] == $params['FROM_USER_ID']
412 )
413 {
414 $result = FavoriteChat::find($params, $this->context);
415 }
416 else
417 {
418 $result = PrivateChat::find($params, $this->context);
419 }
420 break;
421
422 case Chat::IM_TYPE_CHAT:
423 case Chat::IM_TYPE_OPEN:
424 if (
425 isset($params['ENTITY_TYPE'])
426 && $params['ENTITY_TYPE'] == Chat::ENTITY_TYPE_GENERAL
427 )
428 {
430 break;
431 }
432 case Chat::IM_TYPE_OPEN_LINE:
433 $result = Chat::find($params, $this->context);
434 break;
435
436 default:
437 return $result->addError(new ChatError(ChatError::WRONG_TYPE));
438 }
439
440 return $result;
441 }
442
443 private function filterNonCachedFields(array $chat): array
444 {
445 foreach (self::NON_CACHED_FIELDS as $key)
446 {
447 unset($chat[$key]);
448 }
449
450 return $chat;
451 }
452
453 private function getRawById(int $id): ?array
454 {
455 $chat = ChatTable::query()
456 ->setSelect(['*', '_ALIAS' => 'ALIAS.ALIAS'])
457 ->where('ID', $id)
458 ->fetch()
459 ;
460
461 if (!$chat)
462 {
463 return null;
464 }
465
466 $chat['ALIAS'] = $chat['_ALIAS'];
467
468 return $chat;
469 }
470
471 private function getEntityChatId(string $entityType, string $entityId): ?int
472 {
473 $row = ChatTable::query()
474 ->setSelect(['ID'])
475 ->where('ENTITY_TYPE', $entityType)
476 ->where('ENTITY_ID', $entityId)
477 ->setLimit(1)
478 ->fetch()
479 ;
480
481 if (!$row)
482 {
483 return null;
484 }
485
486 return (int)$row['ID'];
487 }
488
489 //endregion
490
491 //region Add new chat
492
497 public function addChat(array $params): Result
498 {
499 $params['ENTITY_TYPE'] ??= '';
500 $params['TYPE'] ??= Chat::IM_TYPE_CHAT;
501
502 // Temporary workaround for Open chat type
503 if (($params['SEARCHABLE'] ?? 'N') === 'Y')
504 {
505 if ($params['TYPE'] === Chat::IM_TYPE_CHAT)
506 {
507 $params['TYPE'] = Chat::IM_TYPE_OPEN;
508 }
509 elseif ($params['TYPE'] === Chat::IM_TYPE_CHANNEL)
510 {
511 $params['TYPE'] = Chat::IM_TYPE_OPEN_CHANNEL;
512 }
513 else
514 {
515 $params['SEARCHABLE'] = 'N';
516 }
517 }
518
519 ChatAnalytics::blockSingleUserEvents();
520
521 $initParams = [
522 'TYPE' => $params['TYPE'] ?? null,
523 'ENTITY_TYPE' => $params['ENTITY_TYPE'] ?? null,
524 'FROM_USER_ID' => $params['FROM_USER_ID'] ?? null,
525 'TO_USER_ID' => $params['TO_USER_ID'] ?? null,
526 ];
527 $chat = $this->initChat($initParams);
528 $addResult = $chat->add($params);
529
530 if ($chat instanceof NullChat)
531 {
532 return $addResult->addError(new ChatError(ChatError::CREATION_ERROR));
533 }
534
535 $resultChat = $addResult->getResult()['CHAT'] ?? null;
536 if ($resultChat instanceof Chat)
537 {
538 (new ChatAnalytics($resultChat))->addSubmitCreateNew();
539 }
540
541 return $addResult;
542 }
543
552 {
553 $result = new Result();
554 if (!isset($params['ENTITY_TYPE']))
555 {
556 return $result->addError(new ChatError(ChatError::ENTITY_TYPE_EMPTY));
557 }
558 if (!isset($params['ENTITY_ID']))
559 {
560 return $result->addError(new ChatError(ChatError::ENTITY_ID_EMPTY));
561 }
562
563 $entityType = (string)$params['ENTITY_TYPE'];
564 $entityId = (string)$params['ENTITY_ID'];
565 $lockName = self::getUniqueAdditionLockName($entityType, $entityId);
566 $connection = Application::getConnection();
567
568 $isLocked = $connection->lock($lockName, self::LOCK_TIMEOUT);
569 if (!$isLocked)
570 {
571 return $result->addError(new ChatError(ChatError::CREATION_ERROR));
572 }
573
574 try
575 {
576 $chatId = $this->getEntityChatId($entityType, $entityId);
577 if ($chatId)
578 {
579 return $result->setResult(['CHAT_ID' => $chatId, 'CHAT' => Chat::getInstance($chatId)]);
580 }
581
582 return $this->addChat($params);
583 }
584 catch (\Throwable $exception)
585 {
586 Application::getInstance()->getExceptionHandler()->writeToLog($exception);
587
588 return $result->addError(new ChatError(ChatError::CREATION_ERROR));
589 }
590 finally
591 {
592 $connection->unlock($lockName);
593 }
594 }
595
596 private static function getUniqueAdditionLockName(string $entityType, string $entityId): string
597 {
598 return "add_unique_chat_{$entityType}_{$entityId}";
599 }
600
601 //endregion
602
603 //region Cache
604
605 public function cleanCache(int $id): void
606 {
607 Application::getInstance()->getCache()->cleanDir($this->getCacheDir($id));
608 }
609
610 protected function getCache(int $id): Cache
611 {
612 $cache = Application::getInstance()->getCache();
613
614 $cacheTTL = defined("BX_COMP_MANAGED_CACHE") ? 18144000 : 1800;
615 $cacheId = "chat_data_{$id}";
616 $cacheDir = $this->getCacheDir($id);
617
618 $cache->initCache($cacheTTL, $cacheId, $cacheDir);
619
620 return $cache;
621 }
622
623 private function getCacheDir(int $id): string
624 {
625 $cacheSubDir = $id % 100;
626
627 return "/bx/imc/chatdata/7/{$cacheSubDir}/{$id}";
628 }
629
630 //endregion
631}
$connection
Определения actionsdefinitions.php:38
$type
Определения options.php:106
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
const ENTITY_TYPE_LIVECHAT
Определения alias.php:13
const ENTITY_TYPE_VIDEOCONF
Определения alias.php:14
static isChatId($id)
Определения common.php:58
static getChatId($dialogId, $userId=null)
Определения dialog.php:93
const CREATION_ERROR
Определения ChatError.php:23
const ENTITY_TYPE_EMPTY
Определения ChatError.php:36
const ENTITY_ID_EMPTY
Определения ChatError.php:35
const WRONG_TYPE
Определения ChatError.php:11
createOpenLineChat(?array $params=null)
Определения ChatFactory.php:290
static self $instance
Определения ChatFactory.php:25
const NON_CACHED_FIELDS
Определения ChatFactory.php:22
createPrivateChat(?array $params=null)
Определения ChatFactory.php:266
cleanCache(int $id)
Определения ChatFactory.php:605
getChat($params)
Определения ChatFactory.php:57
addUniqueChat(array $params)
Определения ChatFactory.php:551
findChat(array $params)
Определения ChatFactory.php:350
isPrivateAiAssistantChat(array $params)
Определения ChatFactory.php:221
getCache(int $id)
Определения ChatFactory.php:610
getNotifyFeed($userId=null)
Определения ChatFactory.php:108
getPrivateChat($fromUserId, $toUserId)
Определения ChatFactory.php:148
AiAssistantService $aiAssistantService
Определения ChatFactory.php:26
initChat(?array $params=null)
Определения ChatFactory.php:184
getPersonalChat($userId=null)
Определения ChatFactory.php:162
getEntityChat(string $entityType, string $entityId)
Определения ChatFactory.php:123
static getInstance()
Определения ChatFactory.php:37
createPersonalChat(?array $params=null)
Определения ChatFactory.php:254
createNotifyFeed(?array $params=null)
Определения ChatFactory.php:242
addChat(array $params)
Определения ChatFactory.php:497
createOpenChat(?array $params=null)
Определения ChatFactory.php:278
static find(array $params=[], ?Context $context=null)
Определения FavoriteChat.php:108
static get()
Определения GeneralChat.php:108
static find(array $params=[], ?Context $context=null)
Определения GeneralChat.php:172
static find(array $params=[], ?Context $context=null)
Определения NotifyChat.php:86
static find(array $params, ?Context $context=null)
Определения PrivateChat.php:372
static getInstance()
Определения application.php:98
Определения result.php:20
static getInstance()
Определения servicelocator.php:33
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
static find(array $filter, array $order, ?int $limit=null, ?Context $context=null)
$entityId
Определения payment.php:4
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799