1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
CopilotChat.php
См. документацию.
1<?php
2
3namespace Bitrix\Im\V2\Chat;
4
5use Bitrix\Im;
6use Bitrix\Im\V2\Chat;
7use Bitrix\Im\V2\Error;
8use Bitrix\Im\V2\Integration\AI\EngineManager;
9use Bitrix\Im\V2\Integration\AI\AIHelper;
10use Bitrix\Im\V2\Integration\AI\CopilotError;
11use Bitrix\Im\V2\Integration\AI\Restriction;
12use Bitrix\Im\V2\Integration\AiAssistant\AiAssistantService;
13use Bitrix\Im\V2\Message;
14use Bitrix\Im\V2\Message\Send\SendingConfig;
15use Bitrix\Im\V2\Message\Send\SendResult;
16use Bitrix\Im\V2\Relation\AddUsersConfig;
17use Bitrix\Im\V2\Relation\DeleteUserConfig;
18use Bitrix\Im\V2\Result;
19use Bitrix\Im\V2\Service\Context;
20use Bitrix\Im\V2\Message\Params;
21use Bitrix\ImBot\Bot;
22use Bitrix\Imbot\Bot\CopilotChatBot;
23use Bitrix\Main\Application;
24use Bitrix\Main\DI\ServiceLocator;
25use Bitrix\Main\Loader;
26use Bitrix\Main\Localization\Loc;
27
29{
30 private const COUNTER_CHAT_CODE = 'copilot_chat_counter';
31 private const COPILOT_ROLE_UPDATED = 'COPILOT_ROLE_UPDATED';
32 private const COPILOT_CONVERSION_LOCK_TIMEOUT = 3;
33
34 public function __construct($source = null)
35 {
36 Loader::includeModule('imbot');
37
38 parent::__construct($source);
39 }
40
41 protected function getDefaultType(): string
42 {
43 return self::IM_TYPE_COPILOT;
44 }
45
46 public function getDefaultManageUsersAdd(): string
47 {
48 return self::MANAGE_RIGHTS_MEMBER;
49 }
50
51 public function getDefaultManageUsersDelete(): string
52 {
53 return self::MANAGE_RIGHTS_MEMBER;
54 }
55
56 public function getDefaultManageSettings(): string
57 {
58 return self::MANAGE_RIGHTS_NONE;
59 }
60
61 public function getManageUsersAdd(): ?string
62 {
63 return $this::MANAGE_RIGHTS_MEMBER;
64 }
65
66 public function getManageUsersDelete(): ?string
67 {
68 return $this::MANAGE_RIGHTS_MEMBER;
69 }
70
71 public function setManageMessages(string $manageMessages): Chat
72 {
73 return $this;
74 }
75
76 public function setManageSettings(string $manageSettings): Chat
77 {
78 return $this;
79 }
80
81 public function setManageUI(string $manageUI): Chat
82 {
83 return $this;
84 }
85
86 public function setManageUsersAdd(string $manageUsersAdd): Chat
87 {
88 return $this;
89 }
90
91 public function setManageUsersDelete(string $manageUsersDelete): Chat
92 {
93 return $this;
94 }
95
96 public function add(array $params, ?Context $context = null): Result
97 {
98 $result = new Result();
99
100 if (!Loader::includeModule('imbot'))
101 {
102 return $result->addError(new ChatError(ChatError::IMBOT_NOT_INSTALLED));
103 }
104
105 if (!self::isAvailable())
106 {
107 return $result->addError(new Error(CopilotError::AI_NOT_AVAILABLE));
108 }
109
110 if (!self::isActive())
111 {
112 return $result->addError(new Error(CopilotError::AI_NOT_ACTIVE));
113 }
114
115 if (Im\V2\Application\Features::isAiAssistantChatCreationAvailable())
116 {
118 }
119
120 $copilotBotId = AIHelper::getCopilotBotId();
121
122 if (!$copilotBotId)
123 {
124 return $result->addError(new Error(ChatError::COPILOT_NOT_INSTALLED));
125 }
126
127 $context ??= new Context();
128 $params['USERS'] = [$context->getUserId(), $copilotBotId];
129
130 return parent::add($params, $context);
131 }
132
133 protected function getValidUsersToAdd(array $userIds): array
134 {
135 $filterUserIds = parent::getValidUsersToAdd($userIds);
136 $copilotChatBot = Loader::includeModule('imbot') ? CopilotChatBot::getBotId() : null;
137
138 if (!isset($copilotChatBot) || !in_array($copilotChatBot, $userIds, true))
139 {
140 return $filterUserIds;
141 }
142
143 if (!in_array($copilotChatBot, $filterUserIds, true))
144 {
145 $filterUserIds[] = CopilotChatBot::getBotId();
146 }
147
148 return $filterUserIds;
149 }
150
151 public function addUsers(array $userIds, AddUsersConfig $config = new AddUsersConfig()): Chat
152 {
153 if (empty($userIds) || !$this->getChatId())
154 {
155 return $this;
156 }
157
158 $usersToAdd = $this->getUsersWithoutBots($userIds);
159
160 return parent::addUsers($usersToAdd, $config);
161 }
162
163 protected function getUsersWithoutBots(array $userIds): array
164 {
165 $usersToAdd = [];
166
167 foreach ($userIds as $userId)
168 {
169 $userId = (int)$userId;
170
172 if ($user->isExist() && $user->isActive() && !$user->isBot())
173 {
174 $usersToAdd[$userId] = $userId;
175 }
176 }
177
178 return $usersToAdd;
179 }
180
181 public function sendMessage(Message $message, ?SendingConfig $sendingConfig = null): SendResult
182 {
183 if (!Im\V2\Application\Features::isAiAssistantChatCreationAvailable())
184 {
185 return parent::sendMessage($message, $sendingConfig);
186 }
187
189 $convertedChat = $result->getResult();
190 if (!$convertedChat || !$result->isSuccess())
191 {
192 return (new SendResult())->addErrors($result->getErrors());
193 }
194
195 return $convertedChat->markAsConverted()->sendMessage($message, $sendingConfig);
196 }
197
201 protected function convertToAssistantChat(): Result
202 {
203 $result = new Result();
204
205 $lockName = $this->getConversionLockName();
206 $connection = Application::getConnection();
207
208 $isLocked = $connection->lock($lockName, self::COPILOT_CONVERSION_LOCK_TIMEOUT);
209 if (!$isLocked)
210 {
211 return $result->addError(new ChatError(ChatError::CREATE_LOCK_ERROR));
212 }
213
214 $updateService = new Chat\Update\UpdateService($this, $this->getUpdateFieldsForConversion());
215
216 try
217 {
218 $convertResult = $updateService->updateChat();
219 $convertedChat = $convertResult->getResult();
220
221 if (!$convertResult->isSuccess())
222 {
223 $result->addErrors($convertResult->getErrors());
224 }
225 else
226 {
227 $result->setResult($convertedChat);
228 }
229 }
230 catch (\Throwable)
231 {
233 }
234 finally
235 {
236 $connection->unlock($lockName);
237
238 return $result;
239 }
240 }
241
242 private function getConversionLockName(): string
243 {
244 return 'convert_copilot_to_assistant_chat_' . $this->getChatId();
245 }
246
247 private function getUpdateFieldsForConversion(): Chat\Update\UpdateFields
248 {
249 $aiAssistantService = ServiceLocator::getInstance()->get(AiAssistantService::class);
250 $aiAssistantBotId = $aiAssistantService->getBotId();
251 $copilotBotId = AIHelper::getCopilotBotId();
252
254 'TYPE' => Chat::IM_TYPE_AI_ASSISTANT,
255 'ADDED_MEMBER_ENTITIES' => [['user', $aiAssistantBotId]],
256 'DELETED_MEMBER_ENTITIES' => [['user', $copilotBotId]],
257 ]);
258 }
259
260 protected function sendMessageUsersAdd(array $usersToAdd, AddUsersConfig $config): void
261 {
262 if (empty($usersToAdd))
263 {
264 return;
265 }
266
267 $oldUsers = array_diff($this->getRelations()->getUserIds(), $usersToAdd);
268 if (count($oldUsers) === 2)
269 {
270 $this->sendAddedUsersBanner();
271 return;
272 }
273
274 if (in_array(Bot\CopilotChatBot::getBotId(), $usersToAdd, true))
275 {
276 unset($usersToAdd[Bot\CopilotChatBot::getBotId()]);
277 }
278
279 parent::sendMessageUsersAdd($usersToAdd, $config);
280 }
281
282 protected function sendGreetingMessage(?int $authorId = null)
283 {
284 return;
285 }
286
287 public function sendBanner(?int $authorId = null, ?string $copilotName = null, ?bool $isUpdate = false): void
288 {
289 if (!isset($copilotName))
290 {
291 $roleManager = (new Im\V2\Integration\AI\RoleManager())->setContextUser($this->getContext()->getUser());
292 $copilotCode = $roleManager->getMainRole($this->getChatId());
293 $copilotName = $roleManager->getRoles([$copilotCode])[$copilotCode]['name'];
294 }
295
297 'MESSAGE_TYPE' => $this->getType(),
298 'TO_CHAT_ID' => $this->getChatId(),
299 'FROM_USER_ID' => Bot\CopilotChatBot::getBotId(),
300 "SYSTEM" => 'Y',
301 'MESSAGE' => $isUpdate
302 ? Loc::getMessage('IM_CHAT_CREATE_COPILOT_WELCOME_UPDATE', ['#COPILOT_NAME#' => $copilotName])
303 : Loc::getMessage('IM_CHAT_CREATE_COPILOT_WELCOME_CREATE', ['#COPILOT_NAME#' => $copilotName])
304 ,
305 'SKIP_USER_CHECK' => 'Y',
306 'PUSH' => 'N',
307 'SKIP_COUNTER_INCREMENTS' => 'Y',
308 'PARAMS' => [
309 Params::COMPONENT_ID => Bot\CopilotChatBot::MESSAGE_COMPONENT_START,
310 Params::COMPONENT_PARAMS => [self::COPILOT_ROLE_UPDATED => $isUpdate],
311 Params::NOTIFY => 'N',
312 Params::COPILOT_ROLE => (new Im\V2\Integration\AI\RoleManager())->getMainRole($this->getId()),
313 ]
314 ]);
315 }
316
317 public function sendAddedUsersBanner(): void
318 {
319 $author = $this->getAuthor();
320 $addedUsers = $this->getRelations()->getUserIds();
321 unset($addedUsers[$author->getId()], $addedUsers[Bot\CopilotChatBot::getBotId()]);
322
323 if (empty($addedUsers))
324 {
325 return;
326 }
327
329 'MESSAGE_TYPE' => $this->getType(),
330 'TO_CHAT_ID' => $this->getChatId(),
331 'FROM_USER_ID' => Bot\CopilotChatBot::getBotId(),
332 "SYSTEM" => 'Y',
333 'MESSAGE' => Loc::getMessage(
334 "IM_CHAT_CREATE_COPILOT_COLLECTIVE_{$author->getGender()}_MSGVER_1",
335 [
336 '#USER_1_NAME#' => htmlspecialcharsback($author->getName()),
337 '#USER_2_NAME#' => $this->getUsersForBanner($addedUsers)
338 ],
339 ),
340 'PUSH' => 'N',
341 'PARAMS' => [
342 Params::COMPONENT_ID => Bot\CopilotChatBot::MESSAGE_COMPONENT_COLLECTIVE,
343 Params::NOTIFY => 'N',
344 Params::COMPONENT_PARAMS => [
345 'AUTHOR_ID' => $author->getId(),
346 'ADDED_USERS' => array_values($addedUsers),
347 ],
348 Params::COPILOT_ROLE => (new Im\V2\Integration\AI\RoleManager())->getMainRole($this->getId()),
349 ],
350 ]);
351 }
352
353 public function containsCopilot(): bool
354 {
355 return true;
356 }
357
358 private function getUsersForBanner(array $addedUsers): string
359 {
360 $userCodes = [];
361 foreach ($addedUsers as $userId)
362 {
363 $userCodes[] = "[USER={$userId}][/USER]";
364 }
365
366 return implode(', ', $userCodes);
367 }
368
369 protected function sendDescriptionMessage(?int $authorId = null): void
370 {
371 return;
372 }
373
374 public function getDescription(): ?string
375 {
376 return null;
377 }
378
379 protected function prepareParams(array $params = []): Result
380 {
381 unset($params['TITLE']);
382
383 return parent::prepareParams($params);
384 }
385
386 public static function getTitleTemplate(): ?string
387 {
388 return Loc::getMessage('IM_CHAT_COPILOT_CHAT_TITLE');
389 }
390
391 protected function generateTitle(): string
392 {
393 $copilotChatCounter = \CUserOptions::GetOption('im', self::COUNTER_CHAT_CODE, 1, $this->getContext()->getUserId());
394 $title = Loc::getMessage('IM_CHAT_COPILOT_CHAT_TITLE', ['#NUMBER#' => $copilotChatCounter]);
395 \CUserOptions::SetOption('im', self::COUNTER_CHAT_CODE, $copilotChatCounter + 1);
396
397 return $title;
398 }
399
400 protected function sendEventUsersAdd(array $usersToAdd): void
401 {
402 if (empty($usersToAdd))
403 {
404 return;
405 }
406
407 foreach ($usersToAdd as $userId)
408 {
409 $relation = $this->getRelations()->getByUserId($userId, $this->getId());
410 if ($relation === null)
411 {
412 continue;
413 }
414 if ($relation->getUser()->isBot())
415 {
416 Im\Bot::onJoinChat('chat'.$this->getId(), [
417 'CHAT_TYPE' => $this->getType(),
418 'MESSAGE_TYPE' => $this->getType(),
419 'BOT_ID' => $userId,
420 'USER_ID' => $this->getContext()->getUserId(),
421 'CHAT_ID' => $this->getId(),
422 'CHAT_AUTHOR_ID' => $this->getAuthorId(),
423 'CHAT_ENTITY_TYPE' => $this->getEntityType(),
424 'CHAT_ENTITY_ID' => $this->getEntityId(),
425 'ACCESS_HISTORY' => (int)$relation->getStartCounter() === 0,
426 'SILENT_JOIN' => 'Y', // suppress system message
427 ]);
428 }
429 }
430 }
431
432 public static function isActive(): bool
433 {
434 return (new Restriction())->isActive();
435 }
436
437 public static function isAvailable(): bool
438 {
439 return (new Restriction())->isAvailable();
440 }
441
443 {
444 if (CopilotChatBot::getBotId() === $userId && $this->getContext()->getUserId() !== $userId)
445 {
446 return (new Result())->addError(new ChatError(ChatError::COPILOT_DELETE_ERROR));
447 }
448
449 return parent::deleteUser($userId, $config);
450 }
451
452 public function toPullFormat(): array
453 {
454 $pull = parent::toPullFormat();
455 $pull['ai_provider'] = EngineManager::getDefaultEngineName();
456
457 return $pull;
458 }
459}
$connection
Определения actionsdefinitions.php:38
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static getType($chatData, bool $camelCase=true)
Определения chat.php:45
const COPILOT_NOT_INSTALLED
Определения ChatError.php:30
const COPILOT_DELETE_ERROR
Определения ChatError.php:31
const IMBOT_NOT_INSTALLED
Определения ChatError.php:28
const CREATE_LOCK_ERROR
Определения ChatError.php:41
sendMessageUsersAdd(array $usersToAdd, AddUsersConfig $config)
Определения CopilotChat.php:260
sendMessage(Message $message, ?SendingConfig $sendingConfig=null)
Определения CopilotChat.php:181
getValidUsersToAdd(array $userIds)
Определения CopilotChat.php:133
setManageUsersDelete(string $manageUsersDelete)
Определения CopilotChat.php:91
static getTitleTemplate()
Определения CopilotChat.php:386
add(array $params, ?Context $context=null)
Определения CopilotChat.php:96
__construct($source=null)
Определения CopilotChat.php:34
addUsers(array $userIds, AddUsersConfig $config=new AddUsersConfig())
Определения CopilotChat.php:151
sendEventUsersAdd(array $usersToAdd)
Определения CopilotChat.php:400
static isAvailable()
Определения CopilotChat.php:437
setManageUI(string $manageUI)
Определения CopilotChat.php:81
setManageSettings(string $manageSettings)
Определения CopilotChat.php:76
getDefaultManageSettings()
Определения CopilotChat.php:56
sendBanner(?int $authorId=null, ?string $copilotName=null, ?bool $isUpdate=false)
Определения CopilotChat.php:287
prepareParams(array $params=[])
Определения CopilotChat.php:379
getUsersWithoutBots(array $userIds)
Определения CopilotChat.php:163
setManageUsersAdd(string $manageUsersAdd)
Определения CopilotChat.php:86
sendDescriptionMessage(?int $authorId=null)
Определения CopilotChat.php:369
sendGreetingMessage(?int $authorId=null)
Определения CopilotChat.php:282
setManageMessages(string $manageMessages)
Определения CopilotChat.php:71
getDefaultManageUsersAdd()
Определения CopilotChat.php:46
static isActive()
Определения CopilotChat.php:432
deleteUser(int $userId, DeleteUserConfig $config=new DeleteUserConfig())
Определения CopilotChat.php:442
getDefaultManageUsersDelete()
Определения CopilotChat.php:51
static create(array $fields)
Определения UpdateFields.php:31
static getInstance(?int $id)
Определения User.php:72
const COPILOT_TO_ASSISTANT_CONVERSION_ERROR
Определения CopilotError.php:20
Определения result.php:20
Определения error.php:15
static Add($arFields)
Определения im_message.php:28
</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
$context
Определения csv_new_setup.php:223
htmlspecialcharsback($str)
Определения tools.php:2693
Определения contextmenu.php:9
Определения Uuid.php:3
Определения ActionUuid.php:3
Определения culture.php:9
$user
Определения mysql_to_pgsql.php:33
$message
Определения payment.php:8
$config
Определения quickway.php:69
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$title
Определения pdf.php:123