170 lines
5.5 KiB
PHP
170 lines
5.5 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace SbpJm\FlarumMSTeamsWebhook\Support;
|
||
|
||
use Flarum\Discussion\Discussion;
|
||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||
use Flarum\Post\Post;
|
||
use Flarum\Settings\SettingsRepositoryInterface;
|
||
use Flarum\User\User;
|
||
|
||
class NotificationPayloadFactory
|
||
{
|
||
public function __construct(
|
||
protected SettingsRepositoryInterface $settings
|
||
) {}
|
||
|
||
/**
|
||
* Build the payload for Microsoft Teams Activity Feed.
|
||
*
|
||
* Informational only (systemDefault).
|
||
* No clickable navigation from the Activity Feed is expected.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
public function make(User $recipient, BlueprintInterface $blueprint): array
|
||
{
|
||
// Activity Feed is informational only
|
||
$activityType = 'systemDefault';
|
||
|
||
$forumName = trim((string) $this->settings->get('forum_title', 'Flarum')) ?: 'Flarum';
|
||
|
||
// Resolve dynamic data
|
||
$discussionTitle = $this->resolveDiscussionTitle($blueprint) ?? $forumName;
|
||
$discussionUrl = $this->resolveSubjectUrl($blueprint)
|
||
?? rtrim((string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''), '/');
|
||
|
||
$previewSnippet = $this->buildPreviewText($blueprint);
|
||
$reasonText = $this->buildReasonTextEn($blueprint);
|
||
|
||
// Teams requires /l/ URLs for topic.source = text
|
||
// /l/browser is the official, neutral deep link for external pages
|
||
$teamsBrowserLink = 'https://teams.microsoft.com/l/browser/' . rawurlencode($discussionUrl);
|
||
|
||
return [
|
||
'topic' => [
|
||
'source' => 'text',
|
||
'value' => $discussionTitle,
|
||
'webUrl' => $teamsBrowserLink,
|
||
],
|
||
'activityType' => $activityType,
|
||
'previewText' => [
|
||
'content' => mb_substr($previewSnippet, 0, 150),
|
||
],
|
||
'templateParameters' => [
|
||
[
|
||
'name' => 'systemDefaultText',
|
||
'value' => $reasonText,
|
||
],
|
||
],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve the discussion title from the notification subject.
|
||
*/
|
||
protected function resolveDiscussionTitle(BlueprintInterface $blueprint): ?string
|
||
{
|
||
$subject = $blueprint->getSubject();
|
||
|
||
if ($subject instanceof Discussion) {
|
||
return (string) $subject->title;
|
||
}
|
||
|
||
if ($subject instanceof Post && $subject->discussion) {
|
||
return (string) $subject->discussion->title;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Resolve the URL to the discussion or specific post.
|
||
*/
|
||
protected function resolveSubjectUrl(BlueprintInterface $blueprint): ?string
|
||
{
|
||
$baseUrl = rtrim(
|
||
(string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''),
|
||
'/'
|
||
);
|
||
|
||
if ($baseUrl === '') {
|
||
return null;
|
||
}
|
||
|
||
$subject = $blueprint->getSubject();
|
||
|
||
if ($subject instanceof Discussion) {
|
||
return sprintf('%s/d/%s', $baseUrl, $subject->id);
|
||
}
|
||
|
||
if ($subject instanceof Post) {
|
||
$discussionId = $subject->discussion_id;
|
||
$postNumber = $subject->number;
|
||
|
||
if ($discussionId !== null && $postNumber !== null) {
|
||
return sprintf('%s/d/%s/%s', $baseUrl, $discussionId, $postNumber);
|
||
}
|
||
}
|
||
|
||
return $baseUrl;
|
||
}
|
||
|
||
/**
|
||
* English reason text for Activity Feed (systemDefault).
|
||
*/
|
||
protected function buildReasonTextEn(BlueprintInterface $blueprint): string
|
||
{
|
||
return match ($blueprint::getType()) {
|
||
'newPost' => 'There was a new reply in a discussion you are following',
|
||
'postMentioned' => 'You were mentioned in a post',
|
||
'userMentioned' => 'You were mentioned',
|
||
'postLiked' => 'Your post was liked',
|
||
'discussionRenamed' => 'A discussion was renamed',
|
||
default => 'New forum activity',
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Build a short preview snippet shown in the Activity Feed.
|
||
*/
|
||
protected function buildPreviewText(BlueprintInterface $blueprint): string
|
||
{
|
||
if ($blueprint::getType() === 'teamsTest') {
|
||
return 'Flarum → Teams test notification sent successfully';
|
||
}
|
||
|
||
$actor = $blueprint->getFromUser();
|
||
$actorName = $actor instanceof User
|
||
? (string) ($actor->display_name ?? $actor->username ?? 'Someone')
|
||
: 'Someone';
|
||
|
||
$type = $blueprint::getType();
|
||
$subject = $blueprint->getSubject();
|
||
|
||
$subjectTitle = null;
|
||
if ($subject instanceof Discussion) {
|
||
$subjectTitle = (string) $subject->title;
|
||
} elseif ($subject instanceof Post && $subject->discussion) {
|
||
$subjectTitle = (string) $subject->discussion->title;
|
||
}
|
||
|
||
$text = match ($type) {
|
||
'newPost' => sprintf('%s replied to a discussion', $actorName),
|
||
'postMentioned' => sprintf('%s mentioned you in a post', $actorName),
|
||
'userMentioned' => sprintf('%s mentioned you', $actorName),
|
||
'postLiked' => sprintf('%s liked your post', $actorName),
|
||
'discussionRenamed' => sprintf('%s renamed a discussion', $actorName),
|
||
default => sprintf('%s: new forum activity', $actorName),
|
||
};
|
||
|
||
if ($subjectTitle) {
|
||
$text .= ' – ' . $subjectTitle;
|
||
}
|
||
|
||
return mb_substr($text, 0, 150);
|
||
}
|
||
}
|