From ae8c751796699165dd31610db01c91949c8cd5f2 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Wed, 10 Nov 2021 21:46:31 -0700 Subject: [PATCH] Update Autospam service, add mark all as read and mark all as not spam options and filter active, spam and not spamreports --- .../Admin/AdminReportController.php | 192 ++++++++++++++++- ..._action_to_account_interstitials_table.php | 44 ++++ resources/views/admin/reports/home.blade.php | 4 +- .../views/admin/reports/show_spam.blade.php | 67 ++++-- resources/views/admin/reports/spam.blade.php | 204 +++++++++++++----- routes/web.php | 1 + 6 files changed, 428 insertions(+), 84 deletions(-) create mode 100644 database/migrations/2021_11_09_105629_add_action_to_account_interstitials_table.php diff --git a/app/Http/Controllers/Admin/AdminReportController.php b/app/Http/Controllers/Admin/AdminReportController.php index d49e0f8d5..484d80b46 100644 --- a/app/Http/Controllers/Admin/AdminReportController.php +++ b/app/Http/Controllers/Admin/AdminReportController.php @@ -95,26 +95,155 @@ trait AdminReportController public function spam(Request $request) { - $appeals = AccountInterstitial::whereType('post.autospam') - ->whereNull('appeal_handled_at') - ->latest() - ->paginate(6); - return view('admin.reports.spam', compact('appeals')); + $this->validate($request, [ + 'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions' + ]); + + $tab = $request->input('tab', 'home'); + + $openCount = Cache::remember('admin-dash:reports:spam-count', 3600, function() { + return AccountInterstitial::whereType('post.autospam') + ->whereNull('appeal_handled_at') + ->count(); + }); + + $monthlyCount = Cache::remember('admin-dash:reports:spam-count:30d', 43200, function() { + return AccountInterstitial::whereType('post.autospam') + ->where('created_at', '>', now()->subMonth()) + ->count(); + }); + + $totalCount = Cache::remember('admin-dash:reports:spam-count:total', 43200, function() { + return AccountInterstitial::whereType('post.autospam')->count(); + }); + + $uncategorized = Cache::remember('admin-dash:reports:spam-sync', 3600, function() { + return AccountInterstitial::whereType('post.autospam') + ->whereIsSpam(null) + ->whereNotNull('appeal_handled_at') + ->exists(); + }); + + $avg = Cache::remember('admin-dash:reports:spam-count:avg', 43200, function() { + if(config('database.default') != 'mysql') { + return 0; + } + return AccountInterstitial::selectRaw('*, count(id) as counter') + ->whereType('post.autospam') + ->groupBy('user_id') + ->get() + ->avg('counter'); + }); + + $avgOpen = Cache::remember('admin-dash:reports:spam-count:avgopen', 43200, function() { + if(config('database.default') != 'mysql') { + return "0"; + } + $seconds = AccountInterstitial::selectRaw('DATE(created_at) AS start_date, AVG(TIME_TO_SEC(TIMEDIFF(appeal_handled_at, created_at))) AS timediff')->whereType('post.autospam')->whereNotNull('appeal_handled_at')->where('created_at', '>', now()->subMonth())->get(); + if(!$seconds) { + return "0"; + } + $mins = floor($seconds->avg('timediff') / 60); + + if($mins < 60) { + return $mins . ' min(s)'; + } + + if($mins < 2880) { + return floor($mins / 60) . ' hour(s)'; + } + + return floor($mins / 60 / 24) . ' day(s)'; + }); + $avgCount = $totalCount && $avg ? floor($totalCount / $avg) : "0"; + + if(in_array($tab, ['home', 'spam', 'not-spam'])) { + $appeals = AccountInterstitial::whereType('post.autospam') + ->when($tab, function($q, $tab) { + switch($tab) { + case 'home': + return $q->whereNull('appeal_handled_at'); + break; + case 'spam': + return $q->whereIsSpam(true); + break; + case 'not-spam': + return $q->whereIsSpam(false); + break; + } + }) + ->latest() + ->paginate(6); + + if($tab !== 'home') { + $appeals = $appeals->appends(['tab' => $tab]); + } + } else { + $appeals = new class { + public function count() { + return 0; + } + + public function render() { + return; + } + }; + } + + + return view('admin.reports.spam', compact('tab', 'appeals', 'openCount', 'monthlyCount', 'totalCount', 'avgCount', 'avgOpen', 'uncategorized')); } public function showSpam(Request $request, $id) { $appeal = AccountInterstitial::whereType('post.autospam') - ->whereNull('appeal_handled_at') ->findOrFail($id); $meta = json_decode($appeal->meta); return view('admin.reports.show_spam', compact('appeal', 'meta')); } + public function fixUncategorizedSpam(Request $request) + { + if(Cache::get('admin-dash:reports:spam-sync-active')) { + return redirect('/i/admin/reports/autospam'); + } + + Cache::put('admin-dash:reports:spam-sync-active', 1, 900); + + AccountInterstitial::chunk(500, function($reports) { + foreach($reports as $report) { + if($report->item_type != 'App\Status') { + continue; + } + + if($report->type != 'post.autospam') { + continue; + } + + if($report->is_spam != null) { + continue; + } + + $status = StatusService::get($report->item_id, false); + if(!$status) { + return; + } + $scope = $status['visibility']; + $report->is_spam = $scope == 'unlisted'; + $report->in_violation = $report->is_spam; + $report->severity_index = 1; + $report->save(); + } + }); + + Cache::forget('admin-dash:reports:spam-sync'); + return redirect('/i/admin/reports/autospam'); + } + public function updateSpam(Request $request, $id) { $this->validate($request, [ - 'action' => 'required|in:dismiss,approve' + 'action' => 'required|in:dismiss,approve,dismiss-all,approve-all' ]); $action = $request->input('action'); @@ -123,15 +252,57 @@ trait AdminReportController ->findOrFail($id); $meta = json_decode($appeal->meta); + $res = ['status' => 'success']; + $now = now(); + Cache::forget('admin-dash:reports:spam-count:total'); + Cache::forget('admin-dash:reports:spam-count:30d'); if($action == 'dismiss') { - $appeal->appeal_handled_at = now(); + $appeal->is_spam = true; + $appeal->appeal_handled_at = $now; $appeal->save(); Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id); Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id); Cache::forget('admin-dash:reports:spam-count'); - return redirect('/i/admin/reports/autospam'); + return $res; + } + + if($action == 'dismiss-all') { + AccountInterstitial::whereType('post.autospam') + ->whereItemType('App\Status') + ->whereNull('appeal_handled_at') + ->whereUserId($appeal->user_id) + ->update(['appeal_handled_at' => $now, 'is_spam' => true]); + Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id); + Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id); + Cache::forget('admin-dash:reports:spam-count'); + return $res; + } + + if($action == 'approve-all') { + AccountInterstitial::whereType('post.autospam') + ->whereItemType('App\Status') + ->whereNull('appeal_handled_at') + ->whereUserId($appeal->user_id) + ->get() + ->each(function($report) use($meta) { + $report->is_spam = false; + $report->appeal_handled_at = now(); + $report->save(); + $status = Status::find($report->item_id); + if($status) { + $status->is_nsfw = $meta->is_nsfw; + $status->scope = 'public'; + $status->visibility = 'public'; + $status->save(); + StatusService::del($status->id); + } + }); + Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id); + Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id); + Cache::forget('admin-dash:reports:spam-count'); + return $res; } $status = $appeal->status; @@ -140,6 +311,7 @@ trait AdminReportController $status->visibility = 'public'; $status->save(); + $appeal->is_spam = false; $appeal->appeal_handled_at = now(); $appeal->save(); @@ -149,7 +321,7 @@ trait AdminReportController Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id); Cache::forget('admin-dash:reports:spam-count'); - return redirect('/i/admin/reports/autospam'); + return $res; } public function updateAppeal(Request $request, $id) diff --git a/database/migrations/2021_11_09_105629_add_action_to_account_interstitials_table.php b/database/migrations/2021_11_09_105629_add_action_to_account_interstitials_table.php new file mode 100644 index 000000000..e08738522 --- /dev/null +++ b/database/migrations/2021_11_09_105629_add_action_to_account_interstitials_table.php @@ -0,0 +1,44 @@ +tinyInteger('severity_index')->unsigned()->nullable()->index(); + $table->boolean('is_spam')->nullable()->index()->after('item_type'); + $table->boolean('in_violation')->nullable()->index()->after('is_spam'); + $table->unsignedInteger('violation_id')->nullable()->index()->after('in_violation'); + $table->boolean('email_notify')->nullable()->index()->after('violation_id'); + $table->bigInteger('thread_id')->unsigned()->unique()->nullable(); + $table->timestamp('emailed_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('account_interstitials', function (Blueprint $table) { + $table->dropColumn('severity_index'); + $table->dropColumn('is_spam'); + $table->dropColumn('in_violation'); + $table->dropColumn('violation_id'); + $table->dropColumn('email_notify'); + $table->dropColumn('thread_id'); + $table->dropColumn('emailed_at'); + }); + } +} diff --git a/resources/views/admin/reports/home.blade.php b/resources/views/admin/reports/home.blade.php index 8f881567d..996ad1b1e 100644 --- a/resources/views/admin/reports/home.blade.php +++ b/resources/views/admin/reports/home.blade.php @@ -16,7 +16,6 @@ - @if($ai || $spam || $mailVerifications)
- @endif @if($reports->count())
@@ -43,7 +41,7 @@
- +

{{$report->type}}

diff --git a/resources/views/admin/reports/show_spam.blade.php b/resources/views/admin/reports/show_spam.blade.php index 9648de03b..970f881ea 100644 --- a/resources/views/admin/reports/show_spam.blade.php +++ b/resources/views/admin/reports/show_spam.blade.php @@ -15,7 +15,7 @@
Unlisted + Content Warning
@if($appeal->has_media) - + @endif
@@ -42,13 +42,15 @@ @endif
-
- @csrf - - -
- -
+ @if($appeal->appeal_handled_at) + @else + + +
+ + + @endif +
@{{$appeal->user->username}} stats
@@ -76,16 +78,43 @@ @push('scripts') -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/admin/reports/spam.blade.php b/resources/views/admin/reports/spam.blade.php index 2e69c09ad..456e38bd1 100644 --- a/resources/views/admin/reports/spam.blade.php +++ b/resources/views/admin/reports/spam.blade.php @@ -1,64 +1,164 @@ @extends('admin.partial.template-full') @section('section') -
-

Autospam

-

Posts flagged as spam

- -
-
-
-
-
-

{{App\AccountInterstitial::whereNull('appeal_handled_at')->whereType('post.autospam')->count()}}

-

active cases

+
+
+
+
+
+

Autospam

+

Automated Spam Detection

+
-
- -
-
-

{{App\AccountInterstitial::whereType('post.autospam')->count()}}

-

total cases

-
-
-
-
- -

{!!$appeals->render()!!}

+ + @if($uncategorized) +
+
+
+
+
+
Uncategorized
+ Reports Found +
+
+
+ +
+
+
+
+ @csrf + +
+
+
+
+ @endif + +
+
+
Total Reports
+ {{$totalCount}} +
+
+
Reports per user
+ {{$avgCount}} +
+
+ +
+
-@endsection \ No newline at end of file +
+
+
+ + +

{!!$appeals->render()!!}

+
+
+
+ +@endsection diff --git a/routes/web.php b/routes/web.php index d0ac72ecb..b66f2dda6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio Route::post('reports/show/{id}', 'AdminController@updateReport'); Route::post('reports/bulk', 'AdminController@bulkUpdateReport'); Route::get('reports/autospam/{id}', 'AdminController@showSpam'); + Route::post('reports/autospam/sync', 'AdminController@fixUncategorizedSpam'); Route::post('reports/autospam/{id}', 'AdminController@updateSpam'); Route::get('reports/autospam', 'AdminController@spam'); Route::get('reports/appeals', 'AdminController@appeals');