Skip to content

Commit

Permalink
feat(bots): Let bots know the parent message when it was a reply
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Feb 6, 2025
1 parent 617f1f9 commit 50a2acc
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 57 deletions.
20 changes: 18 additions & 2 deletions lib/Events/BotInvokeEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,36 @@
use OCP\EventDispatcher\Event;

/**
* @psalm-type ChatMessageParentData = array{
* type: 'Note',
* actor: array{
* type: 'Person',
* id: non-empty-string,
* name: non-empty-string,
* },
* object: array{
* type: 'Note',
* id: numeric-string,
* name: string,
* content: non-empty-string,
* mediaType: 'text/markdown'|'text/plain',
* },
* }
* @psalm-type ChatMessageData = array{
* type: 'Activity'|'Create',
* actor: array{
* type: 'Person',
* id: non-empty-string,
* name: non-empty-string,
* talkParticipantType: non-empty-string,
* talkParticipantType: numeric-string,
* },
* object: array{
* type: 'Note',
* id: non-empty-string,
* id: numeric-string,
* name: string,
* content: non-empty-string,
* mediaType: 'text/markdown'|'text/plain',
* inReplyTo?: ChatMessageParentData,
* },
* target: array{
* type: 'Collection',
Expand Down
87 changes: 87 additions & 0 deletions lib/Service/ActivityPubHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace OCA\Talk\Service;

use OCA\Talk\Events\BotInvokeEvent;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\BotServer;
use OCA\Talk\Model\Message;
use OCA\Talk\Room;
use OCP\Comments\IComment;

/**
* @psalm-import-type ChatMessageParentData from BotInvokeEvent
* @psalm-type NoteType = array{type: 'Note', id: numeric-string, name: string, content: string, mediaType: 'text/markdown'|'text/plain'}
*/
class ActivityPubHelper {
/**
* @return array{type: 'Application', id: non-falsy-string, name: string}
*/
public function generateApplicationFromBot(BotServer $bot): array {
return [
'type' => 'Application',
'id' => Attendee::ACTOR_BOTS . '/' . Attendee::ACTOR_BOT_PREFIX . $bot->getUrlHash(),
'name' => $bot->getName(),
];
}

/**
* @return array{type: 'Collection', id: non-empty-string, name: string}
*/
public function generateCollectionFromRoom(Room $room): array {
/** @var non-empty-string $token */
$token = $room->getToken();
return [
'type' => 'Collection',
'id' => $token,
'name' => $room->getName(),
];
}

/**
* @psalm-param ?ChatMessageParentData $inReplyTo
* @psalm-return NoteType&array{inReplyTo?: ChatMessageParentData}
*/
public function generateNote(IComment $comment, array $messageData, string $messageType, ?array $inReplyTo = null): array {
/** @var string $content */
$content = json_encode($messageData, JSON_THROW_ON_ERROR);
/** @var numeric-string $messageId */
$messageId = $comment->getId();
/** @var 'text/markdown'|'text/plain' $mediaType */
$mediaType = 'text/markdown';// FIXME or text/plain when markdown is disabled
$note = [
'type' => 'Note',
'id' => $messageId,
'name' => $messageType,
'content' => $content,
'mediaType' => $mediaType,
];
if ($inReplyTo !== null) {
$note['inReplyTo'] = $inReplyTo;
}
return $note;
}

/**
* @return array{type: 'Person', id: non-falsy-string, name: string, talkParticipantType: numeric-string}
*/
public function generatePersonFromAttendee(Attendee $attendee): array {
return [
'type' => 'Person',
'id' => $attendee->getActorType() . '/' . $attendee->getActorId(),
'name' => $attendee->getDisplayName(),
'talkParticipantType' => (string)$attendee->getParticipantType(),
];
}

/**
* @return array{type: 'Person', id: non-falsy-string, name: string}
*/
public function generatePersonFromMessageActor(Message $message): array {
return [
'type' => 'Person',
'id' => $message->getActorType() . '/' . $message->getActorId(),
'name' => $message->getActorDisplayName(),
];
}
}
90 changes: 35 additions & 55 deletions lib/Service/BotService.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
* @psalm-import-type InvocationData from BotInvokeEvent
*/
class BotService {
private ActivityPubHelper $activityPubHelper;

public function __construct(
protected BotServerMapper $botServerMapper,
protected BotConversationMapper $botConversationMapper,
Expand All @@ -62,37 +64,22 @@ public function __construct(
protected ICertificateManager $certificateManager,
protected IEventDispatcher $dispatcher,
) {
$this->activityPubHelper = new ActivityPubHelper();
}

public function afterBotEnabled(BotEnabledEvent $event): void {
$this->invokeBots([$event->getBotServer()], $event->getRoom(), null, [
'type' => 'Join',
'actor' => [
'type' => 'Application',
'id' => Attendee::ACTOR_BOTS . '/' . Attendee::ACTOR_BOT_PREFIX . $event->getBotServer()->getUrlHash(),
'name' => $event->getBotServer()->getName(),
],
'object' => [
'type' => 'Collection',
'id' => $event->getRoom()->getToken(),
'name' => $event->getRoom()->getName(),
],
'actor' => $this->activityPubHelper->generateApplicationFromBot($event->getBotServer()),
'object' => $this->activityPubHelper->generateCollectionFromRoom($event->getRoom()),
]);
}

public function afterBotDisabled(BotDisabledEvent $event): void {
$this->invokeBots([$event->getBotServer()], $event->getRoom(), null, [
'type' => 'Leave',
'actor' => [
'type' => 'Application',
'id' => Attendee::ACTOR_BOTS . '/' . Attendee::ACTOR_BOT_PREFIX . $event->getBotServer()->getUrlHash(),
'name' => $event->getBotServer()->getName(),
],
'object' => [
'type' => 'Collection',
'id' => $event->getRoom()->getToken(),
'name' => $event->getRoom()->getName(),
],
'actor' => $this->activityPubHelper->generateApplicationFromBot($event->getBotServer()),
'object' => $this->activityPubHelper->generateCollectionFromRoom($event->getRoom()),
]);
}

Expand All @@ -108,6 +95,28 @@ public function afterChatMessageSent(ChatMessageSentEvent $event, MessageParser
return;
}

$inReplyTo = null;
$parent = $event->getParent();
if ($parent instanceof IComment) {
$parentMessage = $messageParser->createMessage(
$event->getRoom(),
$event->getParticipant(),
$parent,
$this->l10nFactory->get('spreed', 'en', 'en')
);
$messageParser->parseMessage($parentMessage);
$parentMessageData = [
'message' => $parentMessage->getMessage(),
'parameters' => $parentMessage->getMessageParameters(),
];

$inReplyTo = [
'type' => 'Note',
'actor' => $this->activityPubHelper->generatePersonFromMessageActor($parentMessage),
'object' => $this->activityPubHelper->generateNote($parent, $parentMessageData, 'message'),
];
}

$message = $messageParser->createMessage(
$event->getRoom(),
$event->getParticipant(),
Expand All @@ -124,24 +133,9 @@ public function afterChatMessageSent(ChatMessageSentEvent $event, MessageParser

$this->invokeBots($botServers, $event->getRoom(), $event->getComment(), [
'type' => 'Create',
'actor' => [
'type' => 'Person',
'id' => $attendee->getActorType() . '/' . $attendee->getActorId(),
'name' => $attendee->getDisplayName(),
'talkParticipantType' => (string)$attendee->getParticipantType(),
],
'object' => [
'type' => 'Note',
'id' => $event->getComment()->getId(),
'name' => 'message',
'content' => json_encode($messageData, JSON_THROW_ON_ERROR),
'mediaType' => 'text/markdown', // FIXME or text/plain when markdown is disabled
],
'target' => [
'type' => 'Collection',
'id' => $event->getRoom()->getToken(),
'name' => $event->getRoom()->getName(),
]
'actor' => $this->activityPubHelper->generatePersonFromAttendee($attendee),
'object' => $this->activityPubHelper->generateNote($event->getComment(), $messageData, 'message', $inReplyTo),
'target' => $this->activityPubHelper->generateCollectionFromRoom($event->getRoom()),
]);
}

Expand All @@ -167,23 +161,9 @@ public function afterSystemMessageSent(SystemMessageSentEvent $event, MessagePar

$this->invokeBots($botServers, $event->getRoom(), $event->getComment(), [
'type' => 'Activity',
'actor' => [
'type' => 'Person',
'id' => $message->getActorType() . '/' . $message->getActorId(),
'name' => $message->getActorDisplayName(),
],
'object' => [
'type' => 'Note',
'id' => $event->getComment()->getId(),
'name' => $message->getMessageRaw(),
'content' => json_encode($messageData),
'mediaType' => 'text/markdown',
],
'target' => [
'type' => 'Collection',
'id' => $event->getRoom()->getToken(),
'name' => $event->getRoom()->getName(),
]
'actor' => $this->activityPubHelper->generatePersonFromMessageActor($message),
'object' => $this->activityPubHelper->generateNote($event->getComment(), $messageData, $message->getMessageRaw()),
'target' => $this->activityPubHelper->generateCollectionFromRoom($event->getRoom()),
]);
}

Expand Down

0 comments on commit 50a2acc

Please sign in to comment.