123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <?php
- namespace App\Services\Groups;
- use App\Models\Group;
- use App\Models\GroupPost;
- use App\Models\GroupComment;
- use Cache;
- use Purify;
- use Illuminate\Support\Facades\Redis;
- use League\Fractal;
- use App\Util\ActivityPub\Helpers;
- use League\Fractal\Serializer\ArraySerializer;
- use League\Fractal\Pagination\IlluminatePaginatorAdapter;
- use App\Transformer\Api\GroupPostTransformer;
- use App\Services\ActivityPubFetchService;
- use Illuminate\Support\Facades\Validator;
- use App\Rules\ValidUrl;
- class GroupActivityPubService
- {
- const CACHE_KEY = 'pf:services:groups:ap:';
- public static function fetchGroup($url, $saveOnFetch = true)
- {
- $group = Group::where('remote_url', $url)->first();
- if($group) {
- return $group;
- }
- $res = ActivityPubFetchService::get($url);
- if(!$res) {
- return $res;
- }
- $json = json_decode($res, true);
- $group = self::validateGroup($json);
- if(!$group) {
- return false;
- }
- if($saveOnFetch) {
- return self::storeGroup($group);
- }
- return $group;
- }
- public static function fetchGroupPost($url, $saveOnFetch = true)
- {
- $group = GroupPost::where('remote_url', $url)->first();
- if($group) {
- return $group;
- }
- $res = ActivityPubFetchService::get($url);
- if(!$res) {
- return 'invalid res';
- }
- $json = json_decode($res, true);
- if(!$json) {
- return 'invalid json';
- }
- if(isset($json['inReplyTo'])) {
- $comment = self::validateGroupComment($json);
- return self::storeGroupComment($comment);
- }
- $group = self::validateGroupPost($json);
- if($saveOnFetch) {
- return self::storeGroupPost($group);
- }
- return $group;
- }
- public static function validateGroup($obj)
- {
- $validator = Validator::make($obj, [
- '@context' => 'required',
- 'id' => ['required', 'url', new ValidUrl],
- 'type' => 'required|in:Group',
- 'preferredUsername' => 'required',
- 'name' => 'required',
- 'url' => ['sometimes', 'url', new ValidUrl],
- 'inbox' => ['required', 'url', new ValidUrl],
- 'outbox' => ['required', 'url', new ValidUrl],
- 'followers' => ['required', 'url', new ValidUrl],
- 'attributedTo' => 'required',
- 'summary' => 'sometimes',
- 'publicKey' => 'required',
- 'publicKey.id' => 'required',
- 'publicKey.owner' => ['required', 'url', 'same:id', new ValidUrl],
- 'publicKey.publicKeyPem' => 'required',
- ]);
- if($validator->fails()) {
- return false;
- }
- return $validator->validated();
- }
- public static function validateGroupPost($obj)
- {
- $validator = Validator::make($obj, [
- '@context' => 'required',
- 'id' => ['required', 'url', new ValidUrl],
- 'type' => 'required|in:Page,Note',
- 'to' => 'required|array',
- 'to.*' => ['required', 'url', new ValidUrl],
- 'cc' => 'sometimes|array',
- 'cc.*' => ['sometimes', 'url', new ValidUrl],
- 'url' => ['sometimes', 'url', new ValidUrl],
- 'attributedTo' => 'required',
- 'name' => 'sometimes',
- 'target' => 'sometimes',
- 'audience' => 'sometimes',
- 'inReplyTo' => 'sometimes',
- 'content' => 'sometimes',
- 'mediaType' => 'sometimes',
- 'sensitive' => 'sometimes',
- 'attachment' => 'sometimes',
- 'published' => 'required',
- ]);
- if($validator->fails()) {
- return false;
- }
- return $validator->validated();
- }
- public static function validateGroupComment($obj)
- {
- $validator = Validator::make($obj, [
- '@context' => 'required',
- 'id' => ['required', 'url', new ValidUrl],
- 'type' => 'required|in:Note',
- 'to' => 'required|array',
- 'to.*' => ['required', 'url', new ValidUrl],
- 'cc' => 'sometimes|array',
- 'cc.*' => ['sometimes', 'url', new ValidUrl],
- 'url' => ['sometimes', 'url', new ValidUrl],
- 'attributedTo' => 'required',
- 'name' => 'sometimes',
- 'target' => 'sometimes',
- 'audience' => 'sometimes',
- 'inReplyTo' => 'sometimes',
- 'content' => 'sometimes',
- 'mediaType' => 'sometimes',
- 'sensitive' => 'sometimes',
- 'published' => 'required',
- ]);
- if($validator->fails()) {
- return $validator->errors();
- return false;
- }
- return $validator->validated();
- }
- public static function getGroupFromPostActivity($groupPost)
- {
- if(isset($groupPost['audience']) && is_string($groupPost['audience'])) {
- return $groupPost['audience'];
- }
- if(
- isset(
- $groupPost['target'],
- $groupPost['target']['type'],
- $groupPost['target']['attributedTo']
- ) && $groupPost['target']['type'] == 'Collection'
- ) {
- return $groupPost['target']['attributedTo'];
- }
- return false;
- }
- public static function getActorFromPostActivity($groupPost)
- {
- if(!isset($groupPost['attributedTo'])) {
- return false;
- }
- $field = $groupPost['attributedTo'];
- if(is_string($field)) {
- return $field;
- }
- if(is_array($field) && count($field) === 1) {
- if(
- isset(
- $field[0]['id'],
- $field[0]['type']
- ) &&
- $field[0]['type'] === 'Person' &&
- is_string($field[0]['id'])
- ) {
- return $field[0]['id'];
- }
- }
- return false;
- }
- public static function getCaptionFromPostActivity($groupPost)
- {
- if(!isset($groupPost['name']) && isset($groupPost['content'])) {
- return Purify::clean(strip_tags($groupPost['content']));
- }
- if(isset($groupPost['name'], $groupPost['content'])) {
- return Purify::clean(strip_tags($groupPost['name'])) . Purify::clean(strip_tags($groupPost['content']));
- }
- }
- public static function getSensitiveFromPostActivity($groupPost)
- {
- if(!isset($groupPost['sensitive'])) {
- return true;
- }
- if(isset($groupPost['sensitive']) && !is_bool($groupPost['sensitive'])) {
- return true;
- }
- return boolval($groupPost['sensitive']);
- }
- public static function storeGroup($activity)
- {
- $group = new Group;
- $group->profile_id = null;
- $group->category_id = 1;
- $group->name = $activity['name'] ?? 'Untitled Group';
- $group->description = isset($activity['summary']) ? Purify::clean($activity['summary']) : null;
- $group->is_private = false;
- $group->local_only = false;
- $group->metadata = [];
- $group->local = false;
- $group->remote_url = $activity['id'];
- $group->inbox_url = $activity['inbox'];
- $group->activitypub = true;
- $group->save();
- return $group;
- }
- public static function storeGroupPost($groupPost)
- {
- $groupUrl = self::getGroupFromPostActivity($groupPost);
- if(!$groupUrl) {
- return;
- }
- $group = self::fetchGroup($groupUrl, true);
- if(!$group) {
- return;
- }
- $actorUrl = self::getActorFromPostActivity($groupPost);
- $actor = Helpers::profileFetch($actorUrl);
- $caption = self::getCaptionFromPostActivity($groupPost);
- $sensitive = self::getSensitiveFromPostActivity($groupPost);
- $model = GroupPost::firstOrCreate(
- [
- 'remote_url' => $groupPost['id'],
- ], [
- 'group_id' => $group->id,
- 'profile_id' => $actor->id,
- 'type' => 'text',
- 'caption' => $caption,
- 'visibility' => 'public',
- 'is_nsfw' => $sensitive,
- ]
- );
- return $model;
- }
- public static function storeGroupComment($groupPost)
- {
- $groupUrl = self::getGroupFromPostActivity($groupPost);
- if(!$groupUrl) {
- return;
- }
- $group = self::fetchGroup($groupUrl, true);
- if(!$group) {
- return;
- }
- $actorUrl = self::getActorFromPostActivity($groupPost);
- $actor = Helpers::profileFetch($actorUrl);
- $caption = self::getCaptionFromPostActivity($groupPost);
- $sensitive = self::getSensitiveFromPostActivity($groupPost);
- $parentPost = self::fetchGroupPost($groupPost['inReplyTo']);
- $model = GroupComment::firstOrCreate(
- [
- 'remote_url' => $groupPost['id'],
- ], [
- 'group_id' => $group->id,
- 'profile_id' => $actor->id,
- 'status_id' => $parentPost->id,
- 'type' => 'text',
- 'caption' => $caption,
- 'visibility' => 'public',
- 'is_nsfw' => $sensitive,
- 'local' => $actor->private_key != null
- ]
- );
- return $model;
- }
- }
|