Skip to content

Commit

Permalink
Temp
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed May 11, 2023
1 parent 76ee066 commit 438efd4
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 7 deletions.
1 change: 1 addition & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@
include(__DIR__ . '/routes/routesSettingsController.php'),
include(__DIR__ . '/routes/routesSignalingController.php'),
include(__DIR__ . '/routes/routesTempAvatarController.php'),
include(__DIR__ . '/routes/routesWebhookController.php'),
);
36 changes: 36 additions & 0 deletions appinfo/routes/routesWebhookController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023, Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

$requirements = [
'apiVersion' => 'v1',
'token' => '[a-z0-9]{4,30}',
];

return [
'ocs' => [
/** @see \OCA\Talk\Controller\WebhookController::sendMessage() */
['name' => 'Webhook#sendMessage', 'url' => '/api/{apiVersion}/webhook/{token}', 'verb' => 'POST', 'requirements' => $requirements],
],
];
12 changes: 8 additions & 4 deletions lib/Chat/ChatManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public function addChangelogMessage(Room $chat, string $message): IComment {
* Sends a new message to the given chat.
*
* @param Room $chat
* @param Participant $participant
* @param ?Participant $participant
* @param string $actorType
* @param string $actorId
* @param string $message
Expand All @@ -255,7 +255,7 @@ public function addChangelogMessage(Room $chat, string $message): IComment {
* @param string $referenceId
* @return IComment
*/
public function sendMessage(Room $chat, Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId, bool $silent): IComment {
public function sendMessage(Room $chat, ?Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId, bool $silent): IComment {
$comment = $this->commentsManager->create($actorType, $actorId, 'chat', (string) $chat->getId());
$comment->setMessage($message, self::MAX_CHAT_LENGTH);
$comment->setCreationDateTime($creationDateTime);
Expand All @@ -273,15 +273,19 @@ public function sendMessage(Room $chat, Participant $participant, string $actorT
}
$this->setMessageExpiration($chat, $comment);

$event = new ChatParticipantEvent($chat, $comment, $participant, $silent);
if ($participant instanceof Participant) {
$event = new ChatParticipantEvent($chat, $comment, $participant, $silent);
} else {
$event = new ChatEvent($chat, $comment, false, $silent);
}
$this->dispatcher->dispatch(self::EVENT_BEFORE_MESSAGE_SEND, $event);

$shouldFlush = $this->notificationManager->defer();
try {
$this->commentsManager->save($comment);

// Update last_message
if ($comment->getActorType() !== 'bots' || $comment->getActorId() === 'changelog') {
if ($comment->getActorType() !== 'bots' || $comment->getActorId() === 'changelog' || str_starts_with($comment->getActorId(), 'webhook-')) {
$this->roomService->setLastMessage($chat, $comment);
$this->unreadCountCache->clear($chat->getId() . '-');
} else {
Expand Down
257 changes: 257 additions & 0 deletions lib/Controller/WebhookController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
<?php

declare(strict_types=1);
/**
*
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Talk\Controller;

use OCA\Talk\Chat\AutoComplete\SearchPlugin;
use OCA\Talk\Chat\AutoComplete\Sorter;
use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Chat\MessageParser;
use OCA\Talk\Chat\ReactionManager;
use OCA\Talk\Exceptions\UnauthorizedException;
use OCA\Talk\GuestManager;
use OCA\Talk\Manager;
use OCA\Talk\MatterbridgeManager;
use OCA\Talk\Middleware\Attribute\RequireModeratorOrNoLobby;
use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant;
use OCA\Talk\Middleware\Attribute\RequireParticipant;
use OCA\Talk\Middleware\Attribute\RequirePermission;
use OCA\Talk\Middleware\Attribute\RequireReadWriteConversation;
use OCA\Talk\Middleware\Attribute\RequireRoom;
use OCA\Talk\Model\Attachment;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\Message;
use OCA\Talk\Model\Session;
use OCA\Talk\Model\Webhook;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\AttachmentService;
use OCA\Talk\Service\AvatarService;
use OCA\Talk\Service\ChecksumVerificationService;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\SessionService;
use OCA\Talk\Service\WebhookService;
use OCA\Talk\Share\RoomShareProvider;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Collaboration\AutoComplete\IManager;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Comments\IComment;
use OCP\Comments\MessageTooLongException;
use OCP\Comments\NotFoundException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\RichObjectStrings\InvalidObjectExeption;
use OCP\RichObjectStrings\IValidator;
use OCP\Security\ITrustedDomainHelper;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\User\Events\UserLiveStatusEvent;
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;

class WebhookController extends AEnvironmentAwareController {
private ?string $userId;
private IUserManager $userManager;
private IAppManager $appManager;
private ChatManager $chatManager;
private ReactionManager $reactionManager;
private ParticipantService $participantService;
private SessionService $sessionService;
protected AttachmentService $attachmentService;
protected avatarService $avatarService;
private GuestManager $guestManager;
/** @var string[] */
protected array $guestNames;
private MessageParser $messageParser;
protected RoomShareProvider $shareProvider;
private IManager $autoCompleteManager;
private IUserStatusManager $statusManager;
protected MatterbridgeManager $matterbridgeManager;
private SearchPlugin $searchPlugin;
private ISearchResult $searchResult;
protected ITimeFactory $timeFactory;
protected IEventDispatcher $eventDispatcher;
protected IValidator $richObjectValidator;
protected ITrustedDomainHelper $trustedDomainHelper;
private IL10N $l;

public function __construct(
string $appName,
IRequest $request,
ChatManager $chatManager,
ParticipantService $participantService,
AttachmentService $attachmentService,
avatarService $avatarService,
MessageParser $messageParser,
RoomShareProvider $shareProvider,
MatterbridgeManager $matterbridgeManager,
ITimeFactory $timeFactory,
IEventDispatcher $eventDispatcher,
IValidator $richObjectValidator,
ITrustedDomainHelper $trustedDomainHelper,
IL10N $l,
protected ChecksumVerificationService $checksumVerificationService,
protected WebhookService $webhookService,
protected Manager $manager,
) {
parent::__construct($appName, $request);
$this->chatManager = $chatManager;
$this->participantService = $participantService;
$this->attachmentService = $attachmentService;
$this->avatarService = $avatarService;
$this->messageParser = $messageParser;
$this->shareProvider = $shareProvider;
$this->matterbridgeManager = $matterbridgeManager;
$this->timeFactory = $timeFactory;
$this->eventDispatcher = $eventDispatcher;
$this->richObjectValidator = $richObjectValidator;
$this->trustedDomainHelper = $trustedDomainHelper;
$this->l = $l;
}


public function parseCommentToResponse(IComment $comment, Message $parentMessage = null): DataResponse {
$chatMessage = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
$this->messageParser->parseMessage($chatMessage);

if (!$chatMessage->getVisibility()) {
$response = new DataResponse([], Http::STATUS_CREATED);
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
$response->addHeader('X-Chat-Last-Common-Read', (string) $this->chatManager->getLastCommonReadMessage($this->room));
}
return $response;
}

$this->participantService->updateLastReadMessage($this->participant, (int) $comment->getId());

$data = $chatMessage->toArray($this->getResponseFormat());
if ($parentMessage instanceof Message) {
$data['parent'] = $parentMessage->toArray($this->getResponseFormat());
}

$response = new DataResponse($data, Http::STATUS_CREATED);
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
$response->addHeader('X-Chat-Last-Common-Read', (string) $this->chatManager->getLastCommonReadMessage($this->room));
}
return $response;
}

/**
* Sends a new chat message to the given room.
*
* The author and timestamp are automatically set to the current user/guest
* and time.
*
* @param string $token conversation token
* @param string $message the message to send
* @param string $referenceId for the message to be able to later identify it again
* @param int $replyTo Parent id which this message is a reply to
* @param bool $silent If sent silent the chat message will not create any notifications
* @return DataResponse the status code is "201 Created" if successful, and
* "404 Not found" if the room or session for a guest user was not
* found".
*/
#[BruteForceProtection(action: 'webhook')]
#[PublicPage]
public function sendMessage(string $token, string $message, string $referenceId = '', int $replyTo = 0, bool $silent = false): DataResponse {


\OC::$server->getLogger()->error('Entry');
$random = $this->request->getHeader('Talk-Webhook-Random');
if (empty($random) || strlen($random) < 32) {
\OC::$server->getLogger()->error('Wrong Random');
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$checksum = $this->request->getHeader('Talk-Webhook-Checksum');
if (empty($checksum)) {
\OC::$server->getLogger()->error('Wrong Checksum');
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

$webhooks = $this->webhookService->getWebhooksForToken($token);
$webhook = null;
foreach ($webhooks as $webhookAttempt) {
try {
$this->checksumVerificationService->validateRequest(
$random,
$checksum,
$webhookAttempt->getSecret(),
$message
);
$webhook = $webhookAttempt;
break;
} catch (UnauthorizedException) {
}
}

if (!$webhook instanceof Webhook) {
\OC::$server->getLogger()->error('No Webhook found');
$response = new DataResponse([], Http::STATUS_UNAUTHORIZED);
$response->throttle(['action' => 'webhook']);
return $response;
}

$room = $this->manager->getRoomByToken($token);

$actorType = 'bots';
$actorId = 'webhook-' . $webhook->getUrlHash();

$parent = $parentMessage = null;
// if ($replyTo !== 0) {
// try {
// $parent = $this->chatManager->getParentComment($this->room, (string) $replyTo);
// } catch (NotFoundException $e) {
// // Someone is trying to reply cross-rooms or to a non-existing message
// return new DataResponse([], Http::STATUS_BAD_REQUEST);
// }
//
// $parentMessage = $this->messageParser->createMessage($this->room, $this->participant, $parent, $this->l);
// $this->messageParser->parseMessage($parentMessage);
// if (!$parentMessage->isReplyable()) {
// return new DataResponse([], Http::STATUS_BAD_REQUEST);
// }
// }

// $this->participantService->ensureOneToOneRoomIsFilled($this->room);
$creationDateTime = $this->timeFactory->getDateTime('now', new \DateTimeZone('UTC'));

try {
$comment = $this->chatManager->sendMessage($room, $this->participant, $actorType, $actorId, $message, $creationDateTime, $parent, $referenceId, $silent);
} catch (MessageTooLongException) {
return new DataResponse([], Http::STATUS_REQUEST_ENTITY_TOO_LARGE);
} catch (\Exception) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

return new DataResponse([], Http::STATUS_CREATED);
return $this->parseCommentToResponse($comment, $parentMessage);
}
}
3 changes: 3 additions & 0 deletions lib/Events/ChatEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ class ChatEvent extends RoomEvent {
protected IComment $comment;

protected bool $skipLastActivityUpdate;
protected bool $silent;

public function __construct(
Room $room,
IComment $comment,
bool $skipLastActivityUpdate = false,
bool $silent = false,
) {
parent::__construct($room);
$this->comment = $comment;
$this->skipLastActivityUpdate = $skipLastActivityUpdate;
$this->silent = $silent;
}

public function getComment(): IComment {
Expand Down
4 changes: 1 addition & 3 deletions lib/Events/ChatParticipantEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,15 @@

class ChatParticipantEvent extends ChatEvent {
protected Participant $participant;
protected bool $silent;

public function __construct(
Room $room,
IComment $message,
Participant $participant,
bool $silent,
) {
parent::__construct($room, $message);
parent::__construct($room, $message, false, $silent);
$this->participant = $participant;
$this->silent = $silent;
}

public function getParticipant(): Participant {
Expand Down
1 change: 1 addition & 0 deletions lib/Middleware/InjectionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public function beforeController(Controller $controller, string $methodName): vo
if (!$controller instanceof AEnvironmentAwareController) {
return;
}
\OC::$server->getLogger()->error('beforeController');

$reflectionMethod = new \ReflectionMethod($controller, $methodName);

Expand Down
3 changes: 3 additions & 0 deletions lib/Migration/Version18000Date20230504205823.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
$table->addColumn('url', Types::STRING, [
'length' => 4000,
]);
$table->addColumn('url_hash', Types::STRING, [
'length' => 64,
]);
$table->addColumn('description', Types::STRING, [
'length' => 4000,
]);
Expand Down
Loading

0 comments on commit 438efd4

Please sign in to comment.