Merge pull request #5465 from pixelfed/staging

Staging
This commit is contained in:
daniel 2025-01-13 18:17:19 -07:00 committed by GitHub
commit ac4edcd67e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 18 additions and 131 deletions

View file

@ -51,8 +51,6 @@ class PushGatewayRefresh extends Command
$recheck = NotificationAppGatewayService::forceSupportRecheck(); $recheck = NotificationAppGatewayService::forceSupportRecheck();
if ($recheck) { if ($recheck) {
$this->info('Success! Push Notifications are now active!'); $this->info('Success! Push Notifications are now active!');
PushNotificationService::warmList('like');
return; return;
} else { } else {
$this->error('Error, please ensure you have a valid API key.'); $this->error('Error, please ensure you have a valid API key.');

View file

@ -1058,8 +1058,6 @@ class ApiV1Dot1Controller extends Controller
'notify_comment' => false, 'notify_comment' => false,
]); ]);
PushNotificationService::removeMemberFromAll($request->user()->profile_id);
$user = $request->user(); $user = $request->user();
return $this->json([ return $this->json([
@ -1145,31 +1143,15 @@ class ApiV1Dot1Controller extends Controller
if ($request->filled('notify_like')) { if ($request->filled('notify_like')) {
$request->user()->update(['notify_like' => (bool) $request->boolean('notify_like')]); $request->user()->update(['notify_like' => (bool) $request->boolean('notify_like')]);
$request->boolean('notify_like') == true ?
PushNotificationService::set('like', $pid) :
PushNotificationService::removeMember('like', $pid);
} }
if ($request->filled('notify_follow')) { if ($request->filled('notify_follow')) {
$request->user()->update(['notify_follow' => (bool) $request->boolean('notify_follow')]); $request->user()->update(['notify_follow' => (bool) $request->boolean('notify_follow')]);
$request->boolean('notify_follow') == true ?
PushNotificationService::set('follow', $pid) :
PushNotificationService::removeMember('follow', $pid);
} }
if ($request->filled('notify_mention')) { if ($request->filled('notify_mention')) {
$request->user()->update(['notify_mention' => (bool) $request->boolean('notify_mention')]); $request->user()->update(['notify_mention' => (bool) $request->boolean('notify_mention')]);
$request->boolean('notify_mention') == true ?
PushNotificationService::set('mention', $pid) :
PushNotificationService::removeMember('mention', $pid);
} }
if ($request->filled('notify_comment')) { if ($request->filled('notify_comment')) {
$request->user()->update(['notify_comment' => (bool) $request->boolean('notify_comment')]); $request->user()->update(['notify_comment' => (bool) $request->boolean('notify_comment')]);
$request->boolean('notify_comment') == true ?
PushNotificationService::set('comment', $pid) :
PushNotificationService::removeMember('comment', $pid);
}
if ($request->boolean('notify_enabled') == false) {
PushNotificationService::removeMemberFromAll($request->user()->profile_id);
} }
$user = $request->user(); $user = $request->user();

View file

@ -5,11 +5,15 @@ namespace App\Jobs\MentionPipeline;
use App\Mention; use App\Mention;
use App\Notification; use App\Notification;
use App\Status; use App\Status;
use App\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use App\Jobs\PushNotificationPipeline\MentionPushNotifyPipeline;
use App\Services\NotificationAppGatewayService;
use App\Services\PushNotificationService;
use App\Services\StatusService; use App\Services\StatusService;
class MentionPipeline implements ShouldQueue class MentionPipeline implements ShouldQueue
@ -57,7 +61,7 @@ class MentionPipeline implements ShouldQueue
->count(); ->count();
if ($actor->id === $target || $exists !== 0) { if ($actor->id === $target || $exists !== 0) {
return true; return;
} }
Notification::firstOrCreate( Notification::firstOrCreate(
@ -71,5 +75,14 @@ class MentionPipeline implements ShouldQueue
); );
StatusService::del($status->id); StatusService::del($status->id);
if (NotificationAppGatewayService::enabled()) {
if (PushNotificationService::check('mention', $target)) {
$user = User::whereProfileId($target)->first();
if ($user && $user->expo_token && $user->notify_enabled) {
MentionPushNotifyPipeline::dispatch($user->expo_token, $actor->username)->onQueue('pushnotify');
}
}
}
} }
} }

View file

@ -3,121 +3,15 @@
namespace App\Services; namespace App\Services;
use App\User; use App\User;
use Exception;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
use Log;
class PushNotificationService class PushNotificationService {
{
public const ACTIVE_LIST_KEY = 'pf:services:push-notify:active_deliver:';
public const NOTIFY_TYPES = ['follow', 'like', 'mention', 'comment']; public const NOTIFY_TYPES = ['follow', 'like', 'mention', 'comment'];
public const DEEP_CHECK_KEY = 'pf:services:push-notify:deep-check:';
public const PUSH_GATEWAY_VERSION = '1.0'; public const PUSH_GATEWAY_VERSION = '1.0';
public const LOTTERY_ODDS = 20; public static function check($listId, $memberId) {
$user = User::where('notify_enabled', true)->where('profile_id', $memberId)->first();
public const CACHE_LOCK_SECONDS = 10; return $user ? (bool) $user->{"notify_{$listId}"} : false;
public static function get($list)
{
return Redis::smembers(self::ACTIVE_LIST_KEY.$list);
}
public static function set($listId, $memberId)
{
if (! in_array($listId, self::NOTIFY_TYPES)) {
return false;
}
$user = User::whereProfileId($memberId)->first();
if (! $user || $user->status || $user->deleted_at) {
return false;
}
return Redis::sadd(self::ACTIVE_LIST_KEY.$listId, $memberId);
}
public static function check($listId, $memberId)
{
return random_int(1, self::LOTTERY_ODDS) === 1
? self::isMemberDeepCheck($listId, $memberId)
: self::isMember($listId, $memberId);
}
public static function isMember($listId, $memberId)
{
try {
return Redis::sismember(self::ACTIVE_LIST_KEY.$listId, $memberId);
} catch (Exception $e) {
return false;
}
}
public static function isMemberDeepCheck($listId, $memberId)
{
$lock = Cache::lock(self::DEEP_CHECK_KEY.$listId, self::CACHE_LOCK_SECONDS);
try {
$lock->block(5);
$actualCount = User::whereNull('status')->where('notify_enabled', true)->where('notify_'.$listId, true)->count();
$cachedCount = self::count($listId);
if ($actualCount != $cachedCount) {
self::warmList($listId);
$user = User::where('notify_enabled', true)->where('profile_id', $memberId)->first();
return $user ? (bool) $user->{"notify_{$listId}"} : false;
} else {
return self::isMember($listId, $memberId);
}
} catch (Exception $e) {
Log::error('Failed during deep membership check: '.$e->getMessage());
return false;
} finally {
optional($lock)->release();
}
}
public static function removeMember($listId, $memberId)
{
return Redis::srem(self::ACTIVE_LIST_KEY.$listId, $memberId);
}
public static function removeMemberFromAll($memberId)
{
foreach (self::NOTIFY_TYPES as $type) {
self::removeMember($type, $memberId);
}
return 1;
}
public static function count($listId)
{
if (! in_array($listId, self::NOTIFY_TYPES)) {
return false;
}
return Redis::scard(self::ACTIVE_LIST_KEY.$listId);
}
public static function warmList($listId)
{
if (! in_array($listId, self::NOTIFY_TYPES)) {
return false;
}
$key = self::ACTIVE_LIST_KEY.$listId;
Redis::del($key);
foreach (User::where('notify_'.$listId, true)->cursor() as $acct) {
if ($acct->status || $acct->deleted_at || ! $acct->profile_id || ! $acct->notify_enabled) {
continue;
}
Redis::sadd($key, $acct->profile_id);
}
return self::count($listId);
} }
} }