summaryrefslogtreecommitdiffstats
path: root/lib/Service/PostServiceStatus.php
blob: 5770aeaa20e1fa6c974d6bde47510d7f0d6cd48d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php

// Nextcloud - Social Support
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OCA\Social\Service;

use OCA\Social\Entity\Account;
use OCA\Social\Entity\Status;
use OCP\DB\ORM\IEntityManager;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;

class PostServiceStatus {
	private ICache $idempotenceCache;
	private IConfig $config;
	private IEntityManager $entityManager;
	private ProcessMentionsService $mentionsService;
	private FeedManager $feedManager;

	public function __construct(
		ICacheFactory $cacheFactory,
		IConfig $config,
		IEntityManager $entityManager,
		ProcessMentionsService $mentionsService,
		FeedManager $feedManager
	) {
		$this->idempotenceCache = $cacheFactory->createDistributed('social.idempotence');
		$this->config = $config;
		$this->entityManager = $entityManager;
		$this->mentionsService = $mentionsService;
		$this->feedManager = $feedManager;
	}

	/**
	 * @psalm-param array{?text: string, ?spoilerText: string, ?sensitive: bool, ?visibility: Status::STATUS_*} $options
	 */
	public function create(Account $account, array $options): void {
		$this->checkIdempotenceDuplicate($account, $options);

		$status = new Status();
		$status->setText($options['text'] ?? '');
		$status->setSensitive(isset($options['spoilerText'])
			|| ($options['sensitive'] ?? $this->config->getUserValue($account->getUserId(), 'social', 'default_sensitivity', 'no') === 'yes'));
		$status->setAccount($account);
		$status->setLocal(true);

		if (isset($options['inReplyToId'])) {
			$status->setInReplyToId($options['inReplyToId']);
		}

		$visibility = $options['visibility'] ?? $this->config->getUserValue($account->getUserId(), 'social', 'default_privacy', Status::STATUS_PUBLIC);
		if (!in_array($visibility, [Status::STATUS_DIRECT, Status::STATUS_PRIVATE, Status::STATUS_PUBLIC, Status::STATUS_UNLISTED])) {
			throw new ApiException('Invalid visibility');
		}

		// Add mentioned user to CC
		$this->mentionsService->run($status);

		// Save status
		$this->entityManager->persist($account);
		$this->entityManager->flush();

		$this->sendStatus($status);

		$this->updateIdempotency($account, $status);
	}

	private function idempotencyKey(Account $account, string $idempotency): string {
		return $account->getUserId() . '-' . $idempotency;
	}

	private function checkIdempotenceDuplicate(Account $account, array $options): void {
		if (!isset($options['idempotency'])) {
			return;
		}

		if ($this->idempotenceCache->get($this->idempotencyKey($account, $options['idempotency'])) !== null) {
			throw new ApiException('Same message already sent');
		}
	}

	private function updateIdempotency(Account $account, Status $status): void {
		if (!isset($options['idempotency'])) {
			return;
		}

		$this->idempotenceCache->set($this->idempotencyKey($account, $options['idempotency']), $status->getId(), 3600);
	}

	public function sendStatus(Account $account, Status $status): void {
		// to self
		$this->feedManager->addToHome($account->getId(), $status);
	}
}