This repository has been archived on 2026-07-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
SmartAssistant/files/custom/Espo/Modules/SmartAssistant/Services/ConversationRepository.php
T
chaim cf0e9ad2bc fix: stdClass as array error in ConversationRepository
EspoCRM returns JSON data as stdClass objects, not arrays. Using
array access ($data[$i]['role']) on stdClass causes fatal error.
Fixed by using json_decode(json_encode(...), true) to ensure deep
conversion to associative arrays.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:33:25 +00:00

152 lines
6.9 KiB
PHP

<?php
namespace Espo\Modules\SmartAssistant\Services;
use Espo\ORM\EntityManager;
use Espo\Entities\User;
use Espo\Core\Utils\Log;
class ConversationRepository
{
private EntityManager $entityManager;
private User $user;
private Log $log;
public function __construct(EntityManager $entityManager, User $user, Log $log)
{
$this->entityManager = $entityManager;
$this->user = $user;
$this->log = $log;
}
public function startOrResume(string $type, ?string $conversationKey = null, ?string $caseId = null): array
{
if ($conversationKey) {
$entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if ($entity) return ['id' => $entity->get('id'), 'conversationKey' => $conversationKey];
}
$conversationKey = $conversationKey ?: uniqid($type === 'office' ? 'oconv_' : 'conv_', true);
$entity = $this->entityManager->getNewEntity('AssistantConversation');
$entity->set([
'name' => $type === 'office' ? 'שיחה עם העוזר החכם' : 'שיחה עם העוזר',
'conversationType' => $type, 'status' => 'Active',
'conversationKey' => $conversationKey,
'assignedUserId' => $this->user->getId(),
'messageCount' => 0, 'messagesData' => [],
'lastMessageAt' => date('Y-m-d H:i:s'),
]);
if ($caseId && $type === 'case') $entity->set('caseId', $caseId);
$this->entityManager->saveEntity($entity);
return ['id' => $entity->get('id'), 'conversationKey' => $conversationKey];
}
public function appendMessage(string $conversationKey, string $role, string $content, array $actions = []): void
{
$entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) {
$this->startOrResume('office', $conversationKey);
$entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) return;
}
$messages = json_decode(json_encode($entity->get('messagesData') ?? []), true);
$newMessage = ['role' => $role, 'content' => $content, 'timestamp' => date('Y-m-d H:i:s')];
if (!empty($actions)) $newMessage['actions'] = $actions;
$messages[] = $newMessage;
if ($role === 'user' && $entity->get('messageCount') === 0) {
$title = mb_substr($content, 0, 100);
if (mb_strlen($content) > 100) $title .= '...';
$entity->set('name', $title);
}
$entity->set([
'messagesData' => $messages, 'messageCount' => count($messages),
'lastMessageAt' => date('Y-m-d H:i:s'),
'lastMessagePreview' => mb_substr($content, 0, 200),
]);
$this->entityManager->saveEntity($entity);
}
public function getMessages(string $conversationKey): array
{
$entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) return [];
return json_decode(json_encode($entity->get('messagesData') ?? []), true);
}
public function getRecentConversations(?string $type = null, ?string $caseId = null, int $limit = 20): array
{
$where = ['assignedUserId' => $this->user->getId(), 'deleted' => false];
if ($type) $where['conversationType'] = $type;
if ($caseId) $where['caseId'] = $caseId;
$result = [];
foreach ($this->entityManager->getRDBRepository('AssistantConversation')
->select(['id', 'name', 'conversationType', 'conversationKey', 'status', 'messageCount', 'lastMessageAt', 'lastMessagePreview', 'caseId', 'caseName'])
->where($where)->order('lastMessageAt', 'DESC')->limit(0, $limit)->find() as $conv) {
$result[] = [
'id' => $conv->get('id'), 'name' => $conv->get('name'),
'conversationType' => $conv->get('conversationType'),
'conversationKey' => $conv->get('conversationKey'),
'status' => $conv->get('status'), 'messageCount' => $conv->get('messageCount'),
'lastMessageAt' => $conv->get('lastMessageAt'),
'lastMessagePreview' => $conv->get('lastMessagePreview'),
'caseId' => $conv->get('caseId'), 'caseName' => $conv->get('caseName'),
];
}
return $result;
}
public function searchConversations(string $query, ?string $type = null, int $limit = 20): array
{
$where = ['deleted' => false, 'OR' => [['name*' => "%{$query}%"], ['lastMessagePreview*' => "%{$query}%"]]];
if ($type) $where['conversationType'] = $type;
$result = [];
foreach ($this->entityManager->getRDBRepository('AssistantConversation')
->select(['id', 'name', 'conversationType', 'conversationKey', 'messageCount', 'lastMessageAt', 'lastMessagePreview', 'caseId', 'caseName', 'assignedUserName'])
->where($where)->order('lastMessageAt', 'DESC')->limit(0, $limit)->find() as $conv) {
$result[] = [
'id' => $conv->get('id'), 'name' => $conv->get('name'),
'conversationType' => $conv->get('conversationType'),
'conversationKey' => $conv->get('conversationKey'),
'messageCount' => $conv->get('messageCount'),
'lastMessageAt' => $conv->get('lastMessageAt'),
'lastMessagePreview' => $conv->get('lastMessagePreview'),
'caseId' => $conv->get('caseId'), 'caseName' => $conv->get('caseName'),
'assignedUser' => $conv->get('assignedUserName'),
];
}
return $result;
}
public function storePendingActions(string $conversationKey, string $caseId, string $userId, array $actions): void
{
$entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) return;
$data = json_decode(json_encode($entity->get('messagesData') ?? []), true);
for ($i = count($data) - 1; $i >= 0; $i--) {
if (($data[$i]['role'] ?? '') === 'assistant') { $data[$i]['pendingActions'] = $actions; break; }
}
$entity->set('messagesData', $data);
$this->entityManager->saveEntity($entity);
$dir = 'data/tmp/assistant-conversations';
if (!is_dir($dir)) mkdir($dir, 0755, true);
file_put_contents($dir . '/' . $conversationKey . '.json', json_encode([
'caseId' => $caseId, 'userId' => $userId, 'actions' => $actions, 'createdAt' => date('Y-m-d H:i:s'),
]));
}
}