From d516b7998f5b6e87bcef28286b723a1b2a983e7b Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 4 Jan 2025 00:27:39 -0700 Subject: [PATCH] Update StatusTagsPipeline, fix hashtag bug and formatting --- .../StatusPipeline/StatusTagsPipeline.php | 173 +++++++++++------- 1 file changed, 104 insertions(+), 69 deletions(-) diff --git a/app/Jobs/StatusPipeline/StatusTagsPipeline.php b/app/Jobs/StatusPipeline/StatusTagsPipeline.php index 003196e0d..895c3593d 100644 --- a/app/Jobs/StatusPipeline/StatusTagsPipeline.php +++ b/app/Jobs/StatusPipeline/StatusTagsPipeline.php @@ -2,27 +2,28 @@ namespace App\Jobs\StatusPipeline; +use App\Hashtag; +use App\Jobs\MentionPipeline\MentionPipeline; +use App\Mention; +use App\Services\AccountService; +use App\Services\CustomEmojiService; +use App\Services\StatusService; +use App\Services\TrendingHashtagService; +use App\StatusHashtag; +use App\Util\ActivityPub\Helpers; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Services\AccountService; -use App\Services\CustomEmojiService; -use App\Services\StatusService; -use App\Jobs\MentionPipeline\MentionPipeline; -use App\Mention; -use App\Hashtag; -use App\StatusHashtag; -use App\Services\TrendingHashtagService; -use App\Util\ActivityPub\Helpers; +use Illuminate\Support\Facades\DB; class StatusTagsPipeline implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $activity; + protected $status; /** @@ -46,92 +47,126 @@ class StatusTagsPipeline implements ShouldQueue $res = $this->activity; $status = $this->status; - if(isset($res['tag']['type'], $res['tag']['name'])) { + if (isset($res['tag']['type'], $res['tag']['name'])) { $res['tag'] = [$res['tag']]; } $tags = collect($res['tag']); // Emoji - $tags->filter(function($tag) { + $tags->filter(function ($tag) { return $tag && isset($tag['id'], $tag['icon'], $tag['name'], $tag['type']) && $tag['type'] == 'Emoji'; }) - ->map(function($tag) { - CustomEmojiService::import($tag['id'], $this->status->id); - }); + ->map(function ($tag) { + CustomEmojiService::import($tag['id'], $this->status->id); + }); // Hashtags - $tags->filter(function($tag) { + $tags->filter(function ($tag) { return $tag && $tag['type'] == 'Hashtag' && isset($tag['href'], $tag['name']); }) - ->map(function($tag) use($status) { - $name = substr($tag['name'], 0, 1) == '#' ? - substr($tag['name'], 1) : $tag['name']; + ->map(function ($tag) use ($status) { + $name = substr($tag['name'], 0, 1) == '#' ? + substr($tag['name'], 1) : $tag['name']; - $banned = TrendingHashtagService::getBannedHashtagNames(); + $banned = TrendingHashtagService::getBannedHashtagNames(); - if(count($banned)) { - if(in_array(strtolower($name), array_map('strtolower', $banned))) { - return; + if (count($banned)) { + if (in_array(strtolower($name), array_map('strtolower', $banned))) { + return; + } } - } - if(config('database.default') === 'pgsql') { - $hashtag = Hashtag::where('name', 'ilike', $name) - ->orWhere('slug', 'ilike', str_slug($name, '-', false)) - ->first(); + if (config('database.default') === 'pgsql') { + $hashtag = DB::transaction(function () use ($name) { + $baseSlug = str_slug($name, '-', false); + $slug = $baseSlug; + $counter = 1; - if(!$hashtag) { - $hashtag = Hashtag::updateOrCreate([ - 'slug' => str_slug($name, '-', false), - 'name' => $name - ]); + $existing = Hashtag::where('name', $name) + ->lockForUpdate() + ->first(); + + if ($existing) { + if ($existing->slug !== $slug) { + while (Hashtag::where('slug', $slug) + ->where('name', '!=', $name) + ->exists()) { + $slug = $baseSlug.'-'.$counter++; + } + $existing->slug = $slug; + $existing->save(); + } + + return $existing; + } + + while (Hashtag::where('slug', $slug)->exists()) { + $slug = $baseSlug.'-'.$counter++; + } + + return Hashtag::create([ + 'name' => $name, + 'slug' => $slug, + ]); + }); + } else { + $hashtag = DB::transaction(function () use ($name) { + $baseSlug = str_slug($name, '-', false); + $slug = $baseSlug; + $counter = 1; + + while (Hashtag::where('slug', $slug) + ->where('name', '!=', $name) + ->exists()) { + $slug = $baseSlug.'-'.$counter++; + } + + return Hashtag::updateOrCreate( + ['name' => $name], + ['slug' => $slug] + ); + }); } - } else { - $hashtag = Hashtag::updateOrCreate([ - 'slug' => str_slug($name, '-', false), - 'name' => $name + + StatusHashtag::firstOrCreate([ + 'status_id' => $status->id, + 'hashtag_id' => $hashtag->id, + 'profile_id' => $status->profile_id, + 'status_visibility' => $status->scope, ]); - } - - StatusHashtag::firstOrCreate([ - 'status_id' => $status->id, - 'hashtag_id' => $hashtag->id, - 'profile_id' => $status->profile_id, - 'status_visibility' => $status->scope - ]); - }); + }); // Mentions - $tags->filter(function($tag) { + $tags->filter(function ($tag) { return $tag && $tag['type'] == 'Mention' && isset($tag['href']) && substr($tag['href'], 0, 8) === 'https://'; }) - ->map(function($tag) use($status) { - if(Helpers::validateLocalUrl($tag['href'])) { - $parts = explode('/', $tag['href']); - if(!$parts) { - return; + ->map(function ($tag) use ($status) { + if (Helpers::validateLocalUrl($tag['href'])) { + $parts = explode('/', $tag['href']); + if (! $parts) { + return; + } + $pid = AccountService::usernameToId(end($parts)); + if (! $pid) { + return; + } + } else { + $acct = Helpers::profileFetch($tag['href']); + if (! $acct) { + return; + } + $pid = $acct->id; } - $pid = AccountService::usernameToId(end($parts)); - if(!$pid) { - return; - } - } else { - $acct = Helpers::profileFetch($tag['href']); - if(!$acct) { - return; - } - $pid = $acct->id; - } - $mention = new Mention; - $mention->status_id = $status->id; - $mention->profile_id = $pid; - $mention->save(); - MentionPipeline::dispatch($status, $mention); - }); + $mention = new Mention; + $mention->status_id = $status->id; + $mention->profile_id = $pid; + $mention->save(); + MentionPipeline::dispatch($status, $mention); + }); StatusService::refresh($status->id); }