Update mute/block logic with admin defined limits and improved filtering to skip deleted accounts
This commit is contained in:
parent
18940cb209
commit
5b879f0156
4 changed files with 176 additions and 90 deletions
|
@ -17,16 +17,20 @@ use App\{
|
|||
EmailVerification,
|
||||
Follower,
|
||||
FollowRequest,
|
||||
Media,
|
||||
Notification,
|
||||
Profile,
|
||||
User,
|
||||
UserFilter
|
||||
UserDevice,
|
||||
UserFilter,
|
||||
UserSetting
|
||||
};
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use App\Transformer\Api\Mastodon\v1\AccountTransformer;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\NotificationService;
|
||||
use App\Services\UserFilterService;
|
||||
use App\Services\RelationshipService;
|
||||
use App\Jobs\FollowPipeline\FollowAcceptPipeline;
|
||||
|
@ -39,7 +43,8 @@ class AccountController extends Controller
|
|||
'user.block',
|
||||
];
|
||||
|
||||
const FILTER_LIMIT = 'You cannot block or mute more than 100 accounts';
|
||||
const FILTER_LIMIT_MUTE_TEXT = 'You cannot mute more than ';
|
||||
const FILTER_LIMIT_BLOCK_TEXT = 'You cannot block more than ';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
@ -145,16 +150,17 @@ class AccountController extends Controller
|
|||
public function mute(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'type' => 'required|alpha_dash',
|
||||
'type' => 'required|string|in:user',
|
||||
'item' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
$user = Auth::user()->profile;
|
||||
$count = UserFilterService::muteCount($user->id);
|
||||
abort_if($count >= 100, 422, self::FILTER_LIMIT);
|
||||
$pid = $request->user()->profile_id;
|
||||
$count = UserFilterService::muteCount($pid);
|
||||
$maxLimit = intval(config('instance.user_filters.max_user_mutes'));
|
||||
abort_if($count >= $maxLimit, 422, self::FILTER_LIMIT_MUTE_TEXT . $maxLimit . ' accounts');
|
||||
if($count == 0) {
|
||||
$filterCount = UserFilter::whereUserId($user->id)->count();
|
||||
abort_if($filterCount >= 100, 422, self::FILTER_LIMIT);
|
||||
$filterCount = UserFilter::whereUserId($pid)->count();
|
||||
abort_if($filterCount >= $maxLimit, 422, self::FILTER_LIMIT_MUTE_TEXT . $maxLimit . ' accounts');
|
||||
}
|
||||
$type = $request->input('type');
|
||||
$item = $request->input('item');
|
||||
|
@ -167,7 +173,7 @@ class AccountController extends Controller
|
|||
switch ($type) {
|
||||
case 'user':
|
||||
$profile = Profile::findOrFail($item);
|
||||
if ($profile->id == $user->id) {
|
||||
if ($profile->id == $pid) {
|
||||
return abort(403);
|
||||
}
|
||||
$class = get_class($profile);
|
||||
|
@ -177,29 +183,30 @@ class AccountController extends Controller
|
|||
}
|
||||
|
||||
$filter = UserFilter::firstOrCreate([
|
||||
'user_id' => $user->id,
|
||||
'user_id' => $pid,
|
||||
'filterable_id' => $filterable['id'],
|
||||
'filterable_type' => $filterable['type'],
|
||||
'filter_type' => 'mute',
|
||||
]);
|
||||
|
||||
$pid = $user->id;
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("feature:discover:posts:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $profile->id);
|
||||
UserFilterService::mute($pid, $filterable['id']);
|
||||
$res = RelationshipService::refresh($pid, $profile->id);
|
||||
|
||||
return redirect()->back();
|
||||
if($request->wantsJson()) {
|
||||
return response()->json($res);
|
||||
} else {
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
public function unmute(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'type' => 'required|alpha_dash',
|
||||
'type' => 'required|string|in:user',
|
||||
'item' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
$user = Auth::user()->profile;
|
||||
$pid = $request->user()->profile_id;
|
||||
$type = $request->input('type');
|
||||
$item = $request->input('item');
|
||||
$action = $type . '.mute';
|
||||
|
@ -211,7 +218,7 @@ class AccountController extends Controller
|
|||
switch ($type) {
|
||||
case 'user':
|
||||
$profile = Profile::findOrFail($item);
|
||||
if ($profile->id == $user->id) {
|
||||
if ($profile->id == $pid) {
|
||||
return abort(403);
|
||||
}
|
||||
$class = get_class($profile);
|
||||
|
@ -224,24 +231,21 @@ class AccountController extends Controller
|
|||
break;
|
||||
}
|
||||
|
||||
$filter = UserFilter::whereUserId($user->id)
|
||||
$filter = UserFilter::whereUserId($pid)
|
||||
->whereFilterableId($filterable['id'])
|
||||
->whereFilterableType($filterable['type'])
|
||||
->whereFilterType('mute')
|
||||
->first();
|
||||
|
||||
if($filter) {
|
||||
UserFilterService::unmute($pid, $filterable['id']);
|
||||
$filter->delete();
|
||||
}
|
||||
|
||||
$pid = $user->id;
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("feature:discover:posts:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $profile->id);
|
||||
$res = RelationshipService::refresh($pid, $profile->id);
|
||||
|
||||
if($request->wantsJson()) {
|
||||
return response()->json([200]);
|
||||
return response()->json($res);
|
||||
} else {
|
||||
return redirect()->back();
|
||||
}
|
||||
|
@ -250,16 +254,16 @@ class AccountController extends Controller
|
|||
public function block(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'type' => 'required|alpha_dash',
|
||||
'type' => 'required|string|in:user',
|
||||
'item' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
$user = Auth::user()->profile;
|
||||
$count = UserFilterService::blockCount($user->id);
|
||||
abort_if($count >= 100, 422, self::FILTER_LIMIT);
|
||||
$pid = $request->user()->profile_id;
|
||||
$count = UserFilterService::blockCount($pid);
|
||||
$maxLimit = intval(config('instance.user_filters.max_user_blocks'));
|
||||
abort_if($count >= $maxLimit, 422, self::FILTER_LIMIT_BLOCK_TEXT . $maxLimit . ' accounts');
|
||||
if($count == 0) {
|
||||
$filterCount = UserFilter::whereUserId($user->id)->count();
|
||||
abort_if($filterCount >= 100, 422, self::FILTER_LIMIT);
|
||||
$filterCount = UserFilter::whereUserId($pid)->whereFilterType('block')->count();
|
||||
abort_if($filterCount >= $maxLimit, 422, self::FILTER_LIMIT_BLOCK_TEXT . $maxLimit . ' accounts');
|
||||
}
|
||||
$type = $request->input('type');
|
||||
$item = $request->input('item');
|
||||
|
@ -271,41 +275,49 @@ class AccountController extends Controller
|
|||
switch ($type) {
|
||||
case 'user':
|
||||
$profile = Profile::findOrFail($item);
|
||||
if ($profile->id == $user->id || ($profile->user && $profile->user->is_admin == true)) {
|
||||
if ($profile->id == $pid || ($profile->user && $profile->user->is_admin == true)) {
|
||||
return abort(403);
|
||||
}
|
||||
$class = get_class($profile);
|
||||
$filterable['id'] = $profile->id;
|
||||
$filterable['type'] = $class;
|
||||
|
||||
Follower::whereProfileId($profile->id)->whereFollowingId($user->id)->delete();
|
||||
Notification::whereProfileId($user->id)->whereActorId($profile->id)->delete();
|
||||
Follower::whereProfileId($profile->id)->whereFollowingId($pid)->delete();
|
||||
Notification::whereProfileId($pid)
|
||||
->whereActorId($profile->id)
|
||||
->get()
|
||||
->map(function($n) use($pid) {
|
||||
NotificationService::del($pid, $n['id']);
|
||||
$n->forceDelete();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
$filter = UserFilter::firstOrCreate([
|
||||
'user_id' => $user->id,
|
||||
'user_id' => $pid,
|
||||
'filterable_id' => $filterable['id'],
|
||||
'filterable_type' => $filterable['type'],
|
||||
'filter_type' => 'block',
|
||||
]);
|
||||
|
||||
$pid = $user->id;
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $profile->id);
|
||||
UserFilterService::block($pid, $filterable['id']);
|
||||
$res = RelationshipService::refresh($pid, $profile->id);
|
||||
|
||||
return redirect()->back();
|
||||
if($request->wantsJson()) {
|
||||
return response()->json($res);
|
||||
} else {
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
public function unblock(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'type' => 'required|alpha_dash',
|
||||
'type' => 'required|string|in:user',
|
||||
'item' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
$user = Auth::user()->profile;
|
||||
$pid = $request->user()->profile_id;
|
||||
$type = $request->input('type');
|
||||
$item = $request->input('item');
|
||||
$action = $type . '.block';
|
||||
|
@ -316,7 +328,7 @@ class AccountController extends Controller
|
|||
switch ($type) {
|
||||
case 'user':
|
||||
$profile = Profile::findOrFail($item);
|
||||
if ($profile->id == $user->id) {
|
||||
if ($profile->id == $pid) {
|
||||
return abort(403);
|
||||
}
|
||||
$class = get_class($profile);
|
||||
|
@ -330,23 +342,24 @@ class AccountController extends Controller
|
|||
}
|
||||
|
||||
|
||||
$filter = UserFilter::whereUserId($user->id)
|
||||
$filter = UserFilter::whereUserId($pid)
|
||||
->whereFilterableId($filterable['id'])
|
||||
->whereFilterableType($filterable['type'])
|
||||
->whereFilterType('block')
|
||||
->first();
|
||||
|
||||
if($filter) {
|
||||
UserFilterService::unblock($pid, $filterable['id']);
|
||||
$filter->delete();
|
||||
}
|
||||
|
||||
$pid = $user->id;
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("feature:discover:posts:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $profile->id);
|
||||
$res = RelationshipService::refresh($pid, $profile->id);
|
||||
|
||||
return redirect()->back();
|
||||
if($request->wantsJson()) {
|
||||
return response()->json($res);
|
||||
} else {
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
public function followRequests(Request $request)
|
||||
|
@ -513,25 +526,25 @@ class AccountController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
protected function twoFactorBackupCheck($request, $code, User $user)
|
||||
{
|
||||
$backupCodes = $user->{'2fa_backup_codes'};
|
||||
if($backupCodes) {
|
||||
$codes = json_decode($backupCodes, true);
|
||||
foreach ($codes as $c) {
|
||||
if(hash_equals($c, $code)) {
|
||||
$codes = array_flatten(array_diff($codes, [$code]));
|
||||
$user->{'2fa_backup_codes'} = json_encode($codes);
|
||||
$user->save();
|
||||
$request->session()->push('2fa.session.active', true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
protected function twoFactorBackupCheck($request, $code, User $user)
|
||||
{
|
||||
$backupCodes = $user->{'2fa_backup_codes'};
|
||||
if($backupCodes) {
|
||||
$codes = json_decode($backupCodes, true);
|
||||
foreach ($codes as $c) {
|
||||
if(hash_equals($c, $code)) {
|
||||
$codes = array_flatten(array_diff($codes, [$code]));
|
||||
$user->{'2fa_backup_codes'} = json_encode($codes);
|
||||
$user->save();
|
||||
$request->session()->push('2fa.session.active', true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function accountRestored(Request $request)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@ use App\Transformer\Api\{
|
|||
use App\Http\Controllers\FollowerController;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use App\Http\Controllers\AccountController;
|
||||
use App\Http\Controllers\StatusController;
|
||||
|
||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||
|
@ -939,6 +940,25 @@ class ApiV1Controller extends Controller
|
|||
abort(400, 'You cannot block an admin');
|
||||
}
|
||||
|
||||
$count = UserFilterService::blockCount($pid);
|
||||
$maxLimit = intval(config('instance.user_filters.max_user_blocks'));
|
||||
if($count == 0) {
|
||||
$filterCount = UserFilter::whereUserId($pid)
|
||||
->whereFilterType('block')
|
||||
->get()
|
||||
->map(function($rec) {
|
||||
return AccountService::get($rec->filterable_id, true);
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values()
|
||||
->count();
|
||||
abort_if($filterCount >= $maxLimit, 422, AccountController::FILTER_LIMIT_BLOCK_TEXT . $maxLimit . ' accounts');
|
||||
} else {
|
||||
abort_if($count >= $maxLimit, 422, AccountController::FILTER_LIMIT_BLOCK_TEXT . $maxLimit . ' accounts');
|
||||
}
|
||||
|
||||
Follower::whereProfileId($profile->id)->whereFollowingId($pid)->delete();
|
||||
Follower::whereProfileId($pid)->whereFollowingId($profile->id)->delete();
|
||||
Notification::whereProfileId($pid)->whereActorId($profile->id)->delete();
|
||||
|
@ -950,8 +970,6 @@ class ApiV1Controller extends Controller
|
|||
'filter_type' => 'block',
|
||||
]);
|
||||
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $id);
|
||||
|
||||
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
|
||||
|
@ -980,15 +998,17 @@ class ApiV1Controller extends Controller
|
|||
|
||||
$profile = Profile::findOrFail($id);
|
||||
|
||||
UserFilter::whereUserId($pid)
|
||||
$filter = UserFilter::whereUserId($pid)
|
||||
->whereFilterableId($profile->id)
|
||||
->whereFilterableType('App\Profile')
|
||||
->whereFilterType('block')
|
||||
->delete();
|
||||
->first();
|
||||
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $id);
|
||||
if($filter) {
|
||||
$filter->delete();
|
||||
UserFilterService::unblock($pid, $profile->id);
|
||||
RelationshipService::refresh($pid, $id);
|
||||
}
|
||||
|
||||
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
|
@ -1823,6 +1843,25 @@ class ApiV1Controller extends Controller
|
|||
|
||||
$account = Profile::findOrFail($id);
|
||||
|
||||
$count = UserFilterService::muteCount($pid);
|
||||
$maxLimit = intval(config('instance.user_filters.max_user_mutes'));
|
||||
if($count == 0) {
|
||||
$filterCount = UserFilter::whereUserId($pid)
|
||||
->whereFilterType('mute')
|
||||
->get()
|
||||
->map(function($rec) {
|
||||
return AccountService::get($rec->filterable_id, true);
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values()
|
||||
->count();
|
||||
abort_if($filterCount >= $maxLimit, 422, AccountController::FILTER_LIMIT_MUTE_TEXT . $maxLimit . ' accounts');
|
||||
} else {
|
||||
abort_if($count >= $maxLimit, 422, AccountController::FILTER_LIMIT_MUTE_TEXT . $maxLimit . ' accounts');
|
||||
}
|
||||
|
||||
$filter = UserFilter::firstOrCreate([
|
||||
'user_id' => $pid,
|
||||
'filterable_id' => $account->id,
|
||||
|
@ -1830,9 +1869,6 @@ class ApiV1Controller extends Controller
|
|||
'filter_type' => 'mute',
|
||||
]);
|
||||
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("feature:discover:posts:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
RelationshipService::refresh($pid, $id);
|
||||
|
||||
$resource = new Fractal\Resource\Item($account, new RelationshipTransformer());
|
||||
|
@ -1858,23 +1894,21 @@ class ApiV1Controller extends Controller
|
|||
return $this->json(['error' => 'You cannot unmute yourself'], 500);
|
||||
}
|
||||
|
||||
$account = Profile::findOrFail($id);
|
||||
$profile = Profile::findOrFail($id);
|
||||
|
||||
$filter = UserFilter::whereUserId($pid)
|
||||
->whereFilterableId($account->id)
|
||||
->whereFilterableId($profile->id)
|
||||
->whereFilterableType('App\Profile')
|
||||
->whereFilterType('mute')
|
||||
->first();
|
||||
|
||||
if($filter) {
|
||||
$filter->delete();
|
||||
Cache::forget("user:filter:list:$pid");
|
||||
Cache::forget("feature:discover:posts:$pid");
|
||||
Cache::forget("api:local:exp:rec:$pid");
|
||||
UserFilterService::unmute($pid, $profile->id);
|
||||
RelationshipService::refresh($pid, $id);
|
||||
}
|
||||
|
||||
$resource = new Fractal\Resource\Item($account, new RelationshipTransformer());
|
||||
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return $this->json($res);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class UserFilterService
|
|||
public static function mutes(int $profile_id)
|
||||
{
|
||||
$key = self::USER_MUTES_KEY . $profile_id;
|
||||
$warm = Cache::has($key . ':cached');
|
||||
$warm = Cache::has($key . ':cached-v0');
|
||||
if($warm) {
|
||||
return Redis::zrevrange($key, 0, -1) ?? [];
|
||||
} else {
|
||||
|
@ -24,11 +24,22 @@ class UserFilterService
|
|||
$ids = UserFilter::whereFilterType('mute')
|
||||
->whereUserId($profile_id)
|
||||
->pluck('filterable_id')
|
||||
->map(function($id) {
|
||||
$acct = AccountService::get($id, true);
|
||||
if(!$acct) {
|
||||
return false;
|
||||
}
|
||||
return $acct['id'];
|
||||
})
|
||||
->filter(function($res) {
|
||||
return $res;
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
foreach ($ids as $muted_id) {
|
||||
Redis::zadd($key, (int) $muted_id, (int) $muted_id);
|
||||
}
|
||||
Cache::set($key . ':cached', 1, 7776000);
|
||||
Cache::set($key . ':cached-v0', 1, 7776000);
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +47,7 @@ class UserFilterService
|
|||
public static function blocks(int $profile_id)
|
||||
{
|
||||
$key = self::USER_BLOCKS_KEY . $profile_id;
|
||||
$warm = Cache::has($key . ':cached');
|
||||
$warm = Cache::has($key . ':cached-v0');
|
||||
if($warm) {
|
||||
return Redis::zrevrange($key, 0, -1) ?? [];
|
||||
} else {
|
||||
|
@ -46,11 +57,22 @@ class UserFilterService
|
|||
$ids = UserFilter::whereFilterType('block')
|
||||
->whereUserId($profile_id)
|
||||
->pluck('filterable_id')
|
||||
->map(function($id) {
|
||||
$acct = AccountService::get($id, true);
|
||||
if(!$acct) {
|
||||
return false;
|
||||
}
|
||||
return $acct['id'];
|
||||
})
|
||||
->filter(function($res) {
|
||||
return $res;
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
foreach ($ids as $blocked_id) {
|
||||
Redis::zadd($key, (int) $blocked_id, (int) $blocked_id);
|
||||
}
|
||||
Cache::set($key . ':cached', 1, 7776000);
|
||||
Cache::set($key . ':cached-v0', 1, 7776000);
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +84,9 @@ class UserFilterService
|
|||
|
||||
public static function mute(int $profile_id, int $muted_id)
|
||||
{
|
||||
if($profile_id == $muted_id) {
|
||||
return false;
|
||||
}
|
||||
$key = self::USER_MUTES_KEY . $profile_id;
|
||||
$mutes = self::mutes($profile_id);
|
||||
$exists = in_array($muted_id, $mutes);
|
||||
|
@ -73,6 +98,9 @@ class UserFilterService
|
|||
|
||||
public static function unmute(int $profile_id, string $muted_id)
|
||||
{
|
||||
if($profile_id == $muted_id) {
|
||||
return false;
|
||||
}
|
||||
$key = self::USER_MUTES_KEY . $profile_id;
|
||||
$mutes = self::mutes($profile_id);
|
||||
$exists = in_array($muted_id, $mutes);
|
||||
|
@ -84,6 +112,9 @@ class UserFilterService
|
|||
|
||||
public static function block(int $profile_id, int $blocked_id)
|
||||
{
|
||||
if($profile_id == $blocked_id) {
|
||||
return false;
|
||||
}
|
||||
$key = self::USER_BLOCKS_KEY . $profile_id;
|
||||
$exists = in_array($blocked_id, self::blocks($profile_id));
|
||||
if(!$exists) {
|
||||
|
@ -94,6 +125,9 @@ class UserFilterService
|
|||
|
||||
public static function unblock(int $profile_id, string $blocked_id)
|
||||
{
|
||||
if($profile_id == $blocked_id) {
|
||||
return false;
|
||||
}
|
||||
$key = self::USER_BLOCKS_KEY . $profile_id;
|
||||
$exists = in_array($blocked_id, self::blocks($profile_id));
|
||||
if($exists) {
|
||||
|
|
|
@ -107,4 +107,9 @@ return [
|
|||
'admin_invites' => [
|
||||
'enabled' => env('PF_ADMIN_INVITES_ENABLED', true)
|
||||
],
|
||||
|
||||
'user_filters' => [
|
||||
'max_user_blocks' => env('PF_MAX_USER_BLOCKS', 50),
|
||||
'max_user_mutes' => env('PF_MAX_USER_MUTES', 50)
|
||||
]
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue