$images * @param array $imageDescriptions */ public function handle(User $user, string $body, ?Commit $parent = null, ?Commit $quotedCommit = null, array $images = [], array $imageDescriptions = [], bool|string $linkPreview = true, ?NewPoll $poll = null): Commit { $url = match ($linkPreview) { true => $this->urls->first($body), false => null, default => $linkPreview, }; return DB::transaction(function () use ($user, $body, $parent, $quotedCommit, $images, $imageDescriptions, $url, $poll): Commit { $linkPreview = $this->linkPreviewFor($url); $createdAt = now(); $hasGoodReputation = $user->hasGoodReputation(); $commit = $user->commits()->create([ 'body' => $this->emojiShortcodes->replace($body), 'mentions' => $this->mentionedUsers->get($body), 'parent_id' => $parent?->id, 'root_id' => $parent?->threadRootId(), 'status' => $user->isAdmin() || $hasGoodReputation ? CommitStatus::Approved : CommitStatus::Pending, 'link_preview_id' => $linkPreview?->id, 'quoted_commit_id' => $quotedCommit?->id, 'hot_score' => $this->theAlgorithm->score($createdAt, ThreadEngagement::none()), 'created_at' => $createdAt, ]); $this->createCommitImages->handle($commit, $images, $imageDescriptions); if ($poll instanceof NewPoll) { $this->createPoll($commit, $poll); } if (($parent instanceof Commit || $quotedCommit instanceof Commit) && $commit->status === CommitStatus::Approved) { $this->updateCommitHotScores->handle([$commit->id]); } $this->createReplyNotifications->handle($commit); $this->createQuoteNotifications->handle($commit); $this->createMentionNotifications->handle($commit); if ($linkPreview instanceof LinkPreview && $linkPreview->fetched_at === null) { dispatch(new UnfurlLink($linkPreview)); } $this->createWorldReply->handle($commit); if ($commit->status === CommitStatus::Approved && $user->github_id !== CreateTomFriendship::GITHUB_ID ) { $tom = $this->tomQuery->get(); if ($tom instanceof User && $commit->isAddressedTo($tom)) { dispatch(new ReplyAsTom($commit->id)); } } if ($commit->status === CommitStatus::Approved) { dispatch(new CheckUserLevel($user))->afterCommit(); } if ($commit->status === CommitStatus::Pending && ! $hasGoodReputation) { dispatch(new ModerateCommit($commit))->afterCommit(); } if ($commit->body !== '') { dispatch(new EmbedCommit($commit))->afterCommit(); } return $commit; }); } /** * A poll opens with its commit's approval, so polls of pending commits wait for moderation. */ private function createPoll(Commit $commit, NewPoll $newPoll): void { $poll = $commit->poll()->create([ 'duration' => $newPoll->duration, 'opened_at' => $commit->status === CommitStatus::Approved ? now() : null, ]); $poll->options()->createMany(array_map( fn (string $label, int $position): array => ['label' => $this->emojiShortcodes->replace($label), 'position' => $position], $newPoll->options, array_keys($newPoll->options), )); } /** * Commits that link to the same URL share one preview, so a page is fetched once however often it is linked. */ private function linkPreviewFor(?string $url): ?LinkPreview { if ($url === null) { return null; } return LinkPreview::query()->firstOrCreate( ['url_hash' => hash('sha256', $url)], ['url' => $url], ); } }