Skip to content

Commit 1ff0a93

Browse files
authored
Merge pull request #539 from nextcloud/feat/assignment-notify
feat(Assignments): Send notifications for assignment events
2 parents 7a3bdd9 + 62306da commit 1ff0a93

4 files changed

Lines changed: 133 additions & 1 deletion

File tree

lib/AppInfo/Application.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public function register(IRegistrationContext $context): void {
9292
$context->registerEventListener(TaskSuccessfulEvent::class, TaskSuccessfulListener::class);
9393
$context->registerEventListener(TaskFailedEvent::class, TaskFailedListener::class);
9494
$context->registerEventListener(TaskSuccessfulEvent::class, ChattyLLMTaskListener::class);
95+
$context->registerEventListener(TaskFailedEvent::class, ChattyLLMTaskListener::class);
9596
$context->registerEventListener(TaskSuccessfulEvent::class, FileActionTaskSuccessfulListener::class);
9697
$context->registerEventListener(TaskSuccessfulEvent::class, NewFileMenuTaskSuccessfulListener::class);
9798
$context->registerEventListener(TaskFailedEvent::class, FileActionTaskFailedListener::class);

lib/Listener/ChattyLLMTaskListener.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@
1313
use OCA\Assistant\Db\ChattyLLM\Message;
1414
use OCA\Assistant\Db\ChattyLLM\MessageMapper;
1515
use OCA\Assistant\Db\ChattyLLM\SessionMapper;
16+
use OCA\Assistant\Service\NotificationService;
1617
use OCA\Assistant\Service\TaskProcessingService;
1718
use OCP\EventDispatcher\Event;
1819
use OCP\EventDispatcher\IEventListener;
20+
use OCP\TaskProcessing\Events\TaskFailedEvent;
1921
use OCP\TaskProcessing\Events\TaskSuccessfulEvent;
2022
use OCP\TaskProcessing\Task;
2123
use Psr\Log\LoggerInterface;
2224

2325
/**
24-
* @template-implements IEventListener<TaskSuccessfulEvent>
26+
* @template-implements IEventListener<TaskSuccessfulEvent|TaskFailedEvent>
2527
*/
2628
class ChattyLLMTaskListener implements IEventListener {
2729

@@ -30,10 +32,26 @@ public function __construct(
3032
private SessionMapper $sessionMapper,
3133
private TaskProcessingService $taskProcessingService,
3234
private LoggerInterface $logger,
35+
private NotificationService $notificationService,
3336
) {
3437
}
3538

3639
public function handle(Event $event): void {
40+
if ($event instanceof TaskFailedEvent) {
41+
$task = $event->getTask();
42+
$customId = $task->getCustomId();
43+
if (preg_match('/^chatty-llm:(\d+)/', $customId, $matches)) {
44+
$sessionId = (int)$matches[1];
45+
$session = $this->sessionMapper->getUserSession($task->getUserId(), $sessionId);
46+
$assignmentId = $session->getAssignmentId();
47+
if ($assignmentId !== null) {
48+
$this->notificationService->sendAssignmentNotification(
49+
$task->getUserId(), $task, $session
50+
);
51+
}
52+
}
53+
return;
54+
}
3755
if (!($event instanceof TaskSuccessfulEvent)) {
3856
return;
3957
}
@@ -127,6 +145,13 @@ public function handle(Event $event): void {
127145
// Set flag that the conversation summary needs to be regenerated
128146
$session->setIsSummaryUpToDate(false);
129147

148+
$assignmentId = $session->getAssignmentId();
149+
if ($assignmentId !== null) {
150+
$this->notificationService->sendAssignmentNotification(
151+
$task->getUserId(), $task, $session
152+
);
153+
}
154+
130155
$this->sessionMapper->update($session);
131156
}
132157
}

lib/Notification/Notifier.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,81 @@ public function prepare(INotification $notification, string $languageCode): INot
338338

339339
return $notification;
340340

341+
case 'assignment_approval_pending':
342+
$subject = $l->t('Assignment run pending review');
343+
344+
$iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'));
345+
346+
$message = $l->t('"%s" awaits your review before continuing.', [$params['sessionTitle']]);
347+
348+
$notification
349+
->setParsedSubject($subject)
350+
->setParsedMessage($message)
351+
// TODO: link directly to assignment
352+
->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']]))
353+
->setIcon($iconUrl);
354+
355+
$actionLabel = $l->t('View assignment');
356+
$action = $notification->createAction();
357+
$action->setLabel($actionLabel)
358+
->setParsedLabel($actionLabel)
359+
->setLink($notification->getLink(), IAction::TYPE_WEB)
360+
->setPrimary(true);
361+
362+
$notification->addParsedAction($action);
363+
364+
return $notification;
365+
366+
case 'assignment_successful':
367+
$subject = $l->t('Assignment succeeded');
368+
369+
$iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'));
370+
371+
$message = $l->t('"%s" was run successfully.', [$params['sessionTitle']]);
372+
373+
$notification
374+
->setParsedSubject($subject)
375+
->setParsedMessage($message)
376+
// TODO: link directly to assignment
377+
->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']]))
378+
->setIcon($iconUrl);
379+
380+
$actionLabel = $l->t('View assignment result');
381+
$action = $notification->createAction();
382+
$action->setLabel($actionLabel)
383+
->setParsedLabel($actionLabel)
384+
->setLink($notification->getLink(), IAction::TYPE_WEB)
385+
->setPrimary(true);
386+
387+
$notification->addParsedAction($action);
388+
389+
return $notification;
390+
391+
case 'assignment_failure':
392+
$subject = $l->t('Assignment failed');
393+
394+
$iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'));
395+
396+
$message = $l->t('"%s" failed to run.', [$params['sessionTitle']]);
397+
398+
$notification
399+
->setParsedSubject($subject)
400+
->setParsedMessage($message)
401+
// TODO: link directly to assignment
402+
->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']]))
403+
->setIcon($iconUrl);
404+
405+
$actionLabel = $l->t('View assignment result');
406+
$action = $notification->createAction();
407+
$action->setLabel($actionLabel)
408+
->setParsedLabel($actionLabel)
409+
->setLink($notification->getLink(), IAction::TYPE_WEB)
410+
->setPrimary(true);
411+
412+
$notification->addParsedAction($action);
413+
414+
return $notification;
415+
341416
default:
342417
// Unknown subject => Unknown notification => throw
343418
throw new InvalidArgumentException();

lib/Service/NotificationService.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
use DateTime;
1111
use OCA\Assistant\AppInfo\Application;
12+
use OCA\Assistant\Db\ChattyLLM\Session;
1213
use OCP\IURLGenerator;
1314
use OCP\Notification\IManager as INotificationManager;
1415
use OCP\TaskProcessing\Task;
@@ -133,4 +134,34 @@ public function sendNewImageFileNotification(
133134

134135
$manager->notify($notification);
135136
}
137+
138+
public function sendAssignmentNotification(?string $userId, Task $task, Session $session): void {
139+
if ($userId === null) {
140+
return;
141+
}
142+
$manager = $this->notificationManager;
143+
$notification = $manager->createNotification();
144+
145+
$taskSuccessful = $task->getStatus() === Task::STATUS_SUCCESSFUL;
146+
$pendingActions = $session->getAgencyPendingActions();
147+
148+
$params = [
149+
'sessionId' => (string)$session->getId(),
150+
'sessionTitle' => $session->getTitle(),
151+
];
152+
153+
$subject = $taskSuccessful
154+
? ($pendingActions === null
155+
? 'assignment_successful'
156+
: 'assignment_approval_pending')
157+
: 'assignment_failure';
158+
159+
$notification->setApp(Application::APP_ID)
160+
->setUser($userId)
161+
->setDateTime(new DateTime())
162+
->setSubject($subject, $params)
163+
->setObject('session', (string)$session->getId());
164+
165+
$manager->notify($notification);
166+
}
136167
}

0 commit comments

Comments
 (0)