hoffentlich final

This commit is contained in:
JM 2026-05-05 17:20:40 +02:00
parent 60974b68b0
commit 685ca83aaf
1 changed files with 113 additions and 121 deletions

View File

@ -12,57 +12,44 @@ use Flarum\User\User;
class NotificationPayloadFactory class NotificationPayloadFactory
{ {
public function __construct(protected SettingsRepositoryInterface $settings) public function __construct(
{ protected SettingsRepositoryInterface $settings
} ) {}
/** /**
* @return array<string,mixed> * Build the payload for Microsoft Teams Activity Feed.
*/ *
/** * Informational only (systemDefault).
* No clickable navigation from the Activity Feed is expected.
*
* @return array<string, mixed> * @return array<string, mixed>
*/ */
public function make(User $recipient, BlueprintInterface $blueprint): array public function make(User $recipient, BlueprintInterface $blueprint): array
{ {
$activityType = trim((string) $this->settings->get('sbp-jm-msteams-webhook.activity_type', 'systemDefault')); // Activity Feed is informational only
$activityType = 'systemDefault';
$forumName = trim((string) $this->settings->get('forum_title', 'Flarum')) ?: 'Flarum'; $forumName = trim((string) $this->settings->get('forum_title', 'Flarum')) ?: 'Flarum';
$iconId = trim((string) $this->settings->get('sbp-jm-msteams-webhook.icon_id', ''));
// Diskussionstitel + URL dynamisch aus dem Subject ziehen // Resolve dynamic data
// $discussionTitle = $this->resolveDiscussionTitle($blueprint) ?? $forumName; $discussionTitle = $this->resolveDiscussionTitle($blueprint) ?? $forumName;
// $discussionUrl = $this->resolveSubjectUrl($blueprint) ?? 'https://forum.sbp.de'; $discussionUrl = $this->resolveSubjectUrl($blueprint)
// $previewSnippet = $this->buildPreviewText($blueprint); ?? rtrim((string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''), '/');
// $discussionTitle = 'Backup-Strategie für Forum'; $previewSnippet = $this->buildPreviewText($blueprint);
// $discussionUrl = 'https://forum.sbp.de/d/34-which-file-extensions-are-allowed-for-uploads/2'; $reasonText = $this->buildReasonTextEn($blueprint);
// $previewSnippet = 'Bitte prüft, ob wir tägliche Snapshots brauchen.';
// $reasonText = 'Neue Antwort in beobachteter Diskussion';
// Teams requires /l/ URLs for topic.source = text
// /l/browser is the official, neutral deep link for external pages
// // Freie Actor+Reason-Zeile für systemDefault
// $reasonText = match ($blueprint::getType()) {
// 'newPost' => 'There was a new answer in one of your folloed posts',
// 'postMentioned' => 'Your post has been mentioned',
// 'userMentioned' => 'You have been mentioned',
// 'postLiked' => 'Your post has been liked',
// default => 'New sbp forum update',
// };
$discussionTitle = 'Backup-Strategie für Forum';
$discussionUrl = 'https://forum.sbp.de/d/34-which-file-extensions-are-allowed-for-uploads/2';
$previewSnippet = 'Bitte prüft, ob wir tägliche Snapshots brauchen.';
$reasonText = 'Neue Antwort in beobachteter Diskussion';
// ✅ Teams Browser Deep Link (offiziell unterstützt)
$teamsBrowserLink = 'https://teams.microsoft.com/l/browser/' . rawurlencode($discussionUrl); $teamsBrowserLink = 'https://teams.microsoft.com/l/browser/' . rawurlencode($discussionUrl);
$payload = [ return [
'topic' => [ 'topic' => [
'source' => 'text', 'source' => 'text',
'value' => $discussionTitle, 'value' => $discussionTitle,
'webUrl' => $teamsBrowserLink, // ✅ MUSS /l/ sein 'webUrl' => $teamsBrowserLink,
], ],
'activityType' => 'systemDefault', 'activityType' => $activityType,
'previewText' => [ 'previewText' => [
'content' => mb_substr($previewSnippet, 0, 150), 'content' => mb_substr($previewSnippet, 0, 150),
], ],
@ -73,57 +60,11 @@ class NotificationPayloadFactory
], ],
], ],
]; ];
if ($iconId !== '') {
$payload['iconId'] = $iconId;
} }
if (($payload['activityType'] ?? 'systemDefault') !== 'systemDefault') { /**
$payload['templateParameters'] = [ * Resolve the discussion title from the notification subject.
[ */
'name' => 'notificationText',
'value' => mb_substr($previewSnippet, 0, 150),
],
];
}
return $payload;
}
protected function buildPreviewText(BlueprintInterface $blueprint): string
{
if ($blueprint::getType() === 'teamsTest') {
return 'Flarum ➜ Teams Test erfolgreich angestoßen';
}
$actor = $blueprint->getFromUser();
$actorName = $actor instanceof User ? (string) ($actor->display_name ?? $actor->username ?? 'Jemand') : 'Jemand';
$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 hat in einer beobachteten Diskussion geantwortet', $actorName),
'postMentioned' => sprintf('%s hat dich in einem Beitrag erwähnt', $actorName),
'userMentioned' => sprintf('%s hat dich erwähnt', $actorName),
'postLiked' => sprintf('%s hat einen Beitrag von dir geliked', $actorName),
'discussionRenamed' => sprintf('%s hat eine Diskussion umbenannt', $actorName),
default => sprintf('%s: neue Flarum-Benachrichtigung (%s)', $actorName, $type),
};
if ($subjectTitle) {
$text .= ' ' . $subjectTitle;
}
return mb_substr($text, 0, 150);
}
protected function resolveDiscussionTitle(BlueprintInterface $blueprint): ?string protected function resolveDiscussionTitle(BlueprintInterface $blueprint): ?string
{ {
$subject = $blueprint->getSubject(); $subject = $blueprint->getSubject();
@ -138,9 +79,17 @@ class NotificationPayloadFactory
return null; return null;
} }
/**
* Resolve the URL to the discussion or specific post.
*/
protected function resolveSubjectUrl(BlueprintInterface $blueprint): ?string protected function resolveSubjectUrl(BlueprintInterface $blueprint): ?string
{ {
$baseUrl = rtrim((string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''), '/'); $baseUrl = rtrim(
(string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''),
'/'
);
if ($baseUrl === '') { if ($baseUrl === '') {
return null; return null;
} }
@ -163,15 +112,58 @@ class NotificationPayloadFactory
return $baseUrl; return $baseUrl;
} }
protected function buildChainId(User $recipient, BlueprintInterface $blueprint): int /**
* English reason text for Activity Feed (systemDefault).
*/
protected function buildReasonTextEn(BlueprintInterface $blueprint): string
{ {
$subject = $blueprint->getSubject(); return match ($blueprint::getType()) {
$subjectId = is_object($subject) && isset($subject->id) ? (string) $subject->id : '0'; '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',
};
}
return abs(crc32(implode('|', [ /**
(string) $recipient->id, * Build a short preview snippet shown in the Activity Feed.
$blueprint::getType(), */
$subjectId, 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);
} }
} }