1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
HistoryBuilder.php
См. документацию.
1<?php
2
3namespace Bitrix\Im\V2\Integration\AI;
4
5use Bitrix\AI\Context\Memory\Contract;
6use Bitrix\Im\Model\MessageTable;
7use Bitrix\Im\V2\Integration\AI\Dto\File;
8use Bitrix\Im\V2\Integration\AI\Dto\ForwardInfo;
9use Bitrix\Im\V2\Integration\AI\Dto\Message;
10use Bitrix\Im\V2;
11use Bitrix\Im\V2\Message\Params;
12use Bitrix\Imbot\Bot\CopilotChatBot;
13use Bitrix\Main\Loader;
14
15class HistoryBuilder implements Contract\MemoryBuilder
16{
17 private const DEFAULT_LIMIT = 200;
18 private int $chatId;
19 private int $limit = self::DEFAULT_LIMIT;
20 private ?V2\MessageCollection $messages = null;
21 private int $timeInterval;
22 private bool $isMentionListen = true;
23
24 public function __construct(int $chatId)
25 {
26 $this->chatId = $chatId;
27 }
28
29 public function setLimit(int $limit): static
30 {
31 $this->limit = $limit;
32
33 return $this;
34 }
35
36 public function useMergeMode(int $timeInterval): static
37 {
38 $this->timeInterval = $timeInterval;
39
40 return $this;
41 }
42
43 public function useMentionListeningMode(bool $isMentionListen = false): self
44 {
45 $this->isMentionListen = $isMentionListen;
46
47 return $this;
48 }
49
53 public function build(): array
54 {
55 $messageIdsByOrder = $this->getMessageIdsByOrder();
56 $history = [];
57
58 $fetchMoreContext = count($messageIdsByOrder) === $this->limit;
59 $messageIdsByOrder = $this->fillContext($messageIdsByOrder, $fetchMoreContext);
60
61 foreach ($messageIdsByOrder as $messageId)
62 {
63 $message = $this->createMessageDto($messageId);
64 if ($message)
65 {
66 $history[] = $message;
67 }
68 }
69
70 return $history;
71 }
72
73 protected function fillContext(array $messageIdsByOrder, bool $fetchMoreContext = false): array
74 {
75 $this->loadMessages($messageIdsByOrder);
76 $mergeMessageIds = $this->mergeCommonMessages($messageIdsByOrder);
77
78 if (!$fetchMoreContext)
79 {
80 return $mergeMessageIds;
81 }
82
83 $this->limit = (count($messageIdsByOrder) - count($mergeMessageIds)) * 2;
84
85 if ($this->limit === 0)
86 {
87 return $mergeMessageIds;
88 }
89
90 $additionalMessageIds = $this->getMessageIdsByOrder($messageIdsByOrder[count($messageIdsByOrder) - 1]);
91 $this->loadMessages($additionalMessageIds);
92
93 return array_merge($mergeMessageIds, $this->mergeCommonMessages($additionalMessageIds));
94 }
95
96 protected function filterMessages(array $messageIdsByOrder): array
97 {
98 foreach ($messageIdsByOrder as $key => $messageId)
99 {
100 $messageParams = $this->messages[$messageId]->getParams();
101
102 if ($messageParams->isSet(Params::IS_DELETED))
103 {
104 unset($messageIdsByOrder[$key]);
105 }
106
107 if (
108 $messageParams->isSet(Params::COMPONENT_PARAMS)
109 && isset($messageParams->get(Params::COMPONENT_PARAMS)->getValue()['COPILOT_ERROR'])
110 )
111 {
112 unset($messageIdsByOrder[$key]);
113 }
114
115 if ($messageParams->get(Params::COMPONENT_ID)->getValue() === 'ChatCopilotCreationMessage')
116 {
117 unset($messageIdsByOrder[$key]);
118 }
119
120 $messageText = $this->messages[$messageId]->getMessage();
121 if (!$this->isMentionListen && self::checkMessageMentions($this->chatId, $messageText))
122 {
123 unset($messageIdsByOrder[$key]);
124 }
125 }
126
127 return $messageIdsByOrder;
128 }
129
130 protected function mergeCommonMessages(array $messageIdsByOrder): array
131 {
132 $messageIdsByOrder = $this->filterMessages($messageIdsByOrder);
133
134 if (!isset($this->timeInterval))
135 {
136 return $messageIdsByOrder;
137 }
138
139 $userId = 0;
140 $lastMessageId = 0;
141 $lastKey = 0;
142 foreach ($messageIdsByOrder as $key => $messageId)
143 {
144 $currentMessage = $this->messages[$messageId];
145
146 if ($userId === 0)
147 {
148 $userId = $currentMessage->getAuthorId();
149 $lastMessageId = $messageId;
150 $lastKey = $key;
151 continue;
152 }
153
154 $lastMessage = $this->messages[$lastMessageId];
155
156 if (
157 $currentMessage->getAuthorId() !== $userId
158 || $this->checkEntitiesInMessage($currentMessage)
159 || $this->checkEntitiesInMessage($lastMessage)
160 || ($lastMessage->getDateCreate() === null || $currentMessage->getDateCreate() === null)
161 || ($lastMessage->getDateCreate()->getTimestamp() - $currentMessage->getDateCreate()->getTimestamp()) > $this->timeInterval
162 )
163 {
164 $userId = $currentMessage->getAuthorId();
165 $lastMessageId = $messageId;
166 $lastKey = $key;
167
168 continue;
169 }
170
171 $currentMessage->setMessage($currentMessage->getMessage() . ' ' . $lastMessage->getMessage());
172
173 unset($messageIdsByOrder[$lastKey]);
174
175 $lastMessageId = $messageId;
176 $lastKey = $key;
177 $userId = $currentMessage->getAuthorId();
178 }
179
180 return $messageIdsByOrder;
181 }
182
183 protected function checkEntitiesInMessage(V2\Message $message): bool
184 {
185 $messageParams = $message->getParams();
186
187 return $messageParams->isSet(Params::FORWARD_USER_ID)
188 || $messageParams->isSet(Params::REPLY_ID)
189 || $messageParams->isSet(Params::FILE_ID)
190 ;
191 }
192
193 protected function getMessageIdsByOrder(?int $lastMessageId = null): array
194 {
195 $query = MessageTable::query()
196 ->setSelect(['ID'])
197 ->where('CHAT_ID', $this->chatId)
198 ->withNonSystemOnly()
199 ->setLimit($this->limit)
200 ->setOffset(1)
201 ->setOrder(['DATE_CREATE' => 'DESC', 'ID' => 'DESC'])
202 ;
203
204 if (isset($lastMessageId))
205 {
206 $query
207 ->where('ID', '<=', $lastMessageId);
208 }
209
210 return $query->fetchCollection()->getIdList();
211 }
212
213 protected function loadMessages(array $ids): void
214 {
215 $messages = new V2\MessageCollection($ids);
216 $replayedMessageIds = $messages->getReplayedMessageIds();
217 $replayedMessageIds = array_diff($replayedMessageIds, $ids);
218 $replayedMessages = new V2\MessageCollection($replayedMessageIds);
219 $replayedMessages->fillParams();
220 $messages->mergeRegistry($replayedMessages);
221 $messages->fillFiles();
222
223 if ($this->messages === null)
224 {
225 $this->messages = $messages;
226
227 return;
228 }
229
230 foreach ($messages as $message)
231 {
232 if (isset($this->messages[$message->getMessageId()]))
233 {
234 continue;
235 }
236
237 $this->messages->add($message);
238 }
239 }
240
241 protected function createMessageDto(int $messageId, bool $withReply = true): ?Message
242 {
243 $message = $this->messages[$messageId] ?? null;
244 if (!$message)
245 {
246 return null;
247 }
248
249 $reply = $withReply ? $this->getReply($message) : null;
250 $forwardInfo = $this->getForwardInfo($message);
251 $files = $this->getFiles($message);
252
253 return new Message(
255 $this->prepareText($message->getMessage()),
256 $message->getDateCreate(),
257 $message->getAuthor()?->getFullName() ?? '',
258 $forwardInfo,
259 $files,
260 $reply,
261 );
262 }
263
264 protected function prepareText(string $text): string
265 {
266 $text = preg_replace(
267 "/-{54}\n(.[^\[\n]+)\s\[(.+?)\].+?\n(.+?)-{54}/s",
268 '[QUOTE][USER]$1[/USER][DATE]$2[/DATE]$3[/QUOTE]',
269 $text
270 );
271
272 return $text;
273 }
274
276 {
277 if (!$message->getParams()->isSet(Params::FORWARD_USER_ID))
278 {
279 return null;
280 }
281
282 $originalAuthorId = $message->getParams()->get(Params::FORWARD_USER_ID)->getValue();
283 $originalAuthor = V2\Entity\User\User::getInstance($originalAuthorId);
284
285 return new ForwardInfo(
286 $originalAuthor->getName(),
287 );
288 }
289
290 protected function getReply(V2\Message $message): ?Message
291 {
292 $replyId = $message->getParams()->get(Params::REPLY_ID)->getValue();
293
294 if (!$replyId)
295 {
296 return null;
297 }
298
299 return $this->createMessageDto($replyId, false);
300 }
301
302 protected function getFiles(V2\Message $message): array
303 {
304 $files = [];
305
306 foreach ($message->getFiles() as $file)
307 {
308 $diskFile = $file->getDiskFile();
309
310 if (isset($diskFile))
311 {
312 $files[] = new File($diskFile->getName());
313 }
314 }
315
316 return $files;
317 }
318
319 protected static function checkMessageMentions(int $chatId, string $message): bool
320 {
321 if (!Loader::includeModule('imbot'))
322 {
323 return false;
324 }
325
326 $chat = V2\Chat::getInstance($chatId);
327 $relations = $chat->getRelations()->getUsers();
328
329 $forUsers = [];
330 if (preg_match_all("/\[USER=([0-9]+)( REPLACE)?](.*?)\[\/USER]/i", $message, $matches))
331 {
332 foreach ($matches[1] as $userId)
333 {
334 $forUsers[(int)$userId] = (int)$userId;
335 }
336 }
337
338 foreach ($relations as $relation)
339 {
340 if ($relation->getId() === CopilotChatBot::getBotId())
341 {
342 continue;
343 }
344
345 $userId = $relation->getId();
346
347 if (in_array($userId, $forUsers, true))
348 {
349 return true;
350 }
351 }
352
353 return false;
354 }
355}
if(! $messageFields||!isset($messageFields['message_id'])||!isset($messageFields['status'])||!CModule::IncludeModule("messageservice")) $messageId
Определения callback_ismscenter.php:26
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static getInstance(?int $id)
Определения User.php:72
getForwardInfo(V2\Message $message)
Определения HistoryBuilder.php:275
getMessageIdsByOrder(?int $lastMessageId=null)
Определения HistoryBuilder.php:193
static checkMessageMentions(int $chatId, string $message)
Определения HistoryBuilder.php:319
mergeCommonMessages(array $messageIdsByOrder)
Определения HistoryBuilder.php:130
getReply(V2\Message $message)
Определения HistoryBuilder.php:290
fillContext(array $messageIdsByOrder, bool $fetchMoreContext=false)
Определения HistoryBuilder.php:73
createMessageDto(int $messageId, bool $withReply=true)
Определения HistoryBuilder.php:241
useMentionListeningMode(bool $isMentionListen=false)
Определения HistoryBuilder.php:43
useMergeMode(int $timeInterval)
Определения HistoryBuilder.php:36
checkEntitiesInMessage(V2\Message $message)
Определения HistoryBuilder.php:183
getFiles(V2\Message $message)
Определения HistoryBuilder.php:302
filterMessages(array $messageIdsByOrder)
Определения HistoryBuilder.php:96
static getInstance()
Определения application.php:98
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$query
Определения get_search.php:11
Определения Uuid.php:3
Определения ActionUuid.php:3
Определения Image.php:9
$files
Определения mysql_to_pgsql.php:30
$message
Определения payment.php:8
if(empty($signedUserToken)) $key
Определения quickway.php:257
$text
Определения template_pdf.php:79
</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
$matches
Определения index.php:22