diff --git a/app/Services/PushNotificationService.php b/app/Services/PushNotificationService.php new file mode 100644 index 000000000..8acb07acc --- /dev/null +++ b/app/Services/PushNotificationService.php @@ -0,0 +1,123 @@ +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); + } +}