|
@@ -0,0 +1,255 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Http\Controllers\Admin;
|
|
|
+
|
|
|
+use DB, Cache;
|
|
|
+use App\{
|
|
|
+ AccountInterstitial,
|
|
|
+ DiscoverCategory,
|
|
|
+ DiscoverCategoryHashtag,
|
|
|
+ Hashtag,
|
|
|
+ Media,
|
|
|
+ Profile,
|
|
|
+ Status,
|
|
|
+ StatusHashtag,
|
|
|
+ User
|
|
|
+};
|
|
|
+use App\Models\ConfigCache;
|
|
|
+use App\Models\AutospamCustomTokens;
|
|
|
+use App\Services\AccountService;
|
|
|
+use App\Services\ConfigCacheService;
|
|
|
+use App\Services\StatusService;
|
|
|
+use Carbon\Carbon;
|
|
|
+use Illuminate\Http\Request;
|
|
|
+use Illuminate\Validation\Rule;
|
|
|
+use League\ISO3166\ISO3166;
|
|
|
+use Illuminate\Support\Str;
|
|
|
+use Illuminate\Support\Facades\Storage;
|
|
|
+use Illuminate\Support\Facades\Validator;
|
|
|
+use Illuminate\Support\Facades\Http;
|
|
|
+use App\Http\Controllers\PixelfedDirectoryController;
|
|
|
+use \DateInterval;
|
|
|
+use \DatePeriod;
|
|
|
+use App\Http\Resources\AdminSpamReport;
|
|
|
+use App\Util\Lexer\Classifier;
|
|
|
+use App\Jobs\AutospamPipeline\AutospamPretrainPipeline;
|
|
|
+use App\Jobs\AutospamPipeline\AutospamPretrainNonSpamPipeline;
|
|
|
+use App\Jobs\AutospamPipeline\AutospamUpdateCachedDataPipeline;
|
|
|
+use Illuminate\Support\Facades\URL;
|
|
|
+use App\Services\AutospamService;
|
|
|
+
|
|
|
+trait AdminAutospamController
|
|
|
+{
|
|
|
+ public function autospamHome(Request $request)
|
|
|
+ {
|
|
|
+ return view('admin.autospam.home');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getAutospamConfigApi(Request $request)
|
|
|
+ {
|
|
|
+ $open = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
|
|
|
+ return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
|
|
|
+ });
|
|
|
+
|
|
|
+ $closed = Cache::remember('admin-dash:reports:spam-count-closed', 3600, function() {
|
|
|
+ return AccountInterstitial::whereType('post.autospam')->whereNotNull('appeal_handled_at')->count();
|
|
|
+ });
|
|
|
+
|
|
|
+ $thisWeek = Cache::remember('admin-dash:reports:spam-count-stats-this-week ', 86400, function() {
|
|
|
+ $sr = config('database.default') == 'pgsql' ? "to_char(created_at, 'MM-YYYY')" : "DATE_FORMAT(created_at, '%m-%Y')";
|
|
|
+ $gb = config('database.default') == 'pgsql' ? [DB::raw($sr)] : DB::raw($sr);
|
|
|
+ $s = AccountInterstitial::select(
|
|
|
+ DB::raw('count(id) as count'),
|
|
|
+ DB::raw($sr . " as month_year")
|
|
|
+ )
|
|
|
+ ->where('created_at', '>=', now()->subWeeks(52))
|
|
|
+ ->groupBy($gb)
|
|
|
+ ->get()
|
|
|
+ ->map(function($s) {
|
|
|
+ $dt = now()->parse('01-' . $s->month_year);
|
|
|
+ return [
|
|
|
+ 'id' => $dt->format('Ym'),
|
|
|
+ 'x' => $dt->format('M Y'),
|
|
|
+ 'y' => $s->count
|
|
|
+ ];
|
|
|
+ })
|
|
|
+ ->sortBy('id')
|
|
|
+ ->values()
|
|
|
+ ->toArray();
|
|
|
+ return $s;
|
|
|
+ });
|
|
|
+
|
|
|
+ $files = [
|
|
|
+ 'spam' => [
|
|
|
+ 'exists' => Storage::exists(AutospamService::MODEL_SPAM_PATH),
|
|
|
+ 'size' => 0
|
|
|
+ ],
|
|
|
+ 'ham' => [
|
|
|
+ 'exists' => Storage::exists(AutospamService::MODEL_HAM_PATH),
|
|
|
+ 'size' => 0
|
|
|
+ ],
|
|
|
+ 'combined' => [
|
|
|
+ 'exists' => Storage::exists(AutospamService::MODEL_FILE_PATH),
|
|
|
+ 'size' => 0
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+
|
|
|
+ if($files['spam']['exists']) {
|
|
|
+ $files['spam']['size'] = Storage::size(AutospamService::MODEL_SPAM_PATH);
|
|
|
+ }
|
|
|
+
|
|
|
+ if($files['ham']['exists']) {
|
|
|
+ $files['ham']['size'] = Storage::size(AutospamService::MODEL_HAM_PATH);
|
|
|
+ }
|
|
|
+
|
|
|
+ if($files['combined']['exists']) {
|
|
|
+ $files['combined']['size'] = Storage::size(AutospamService::MODEL_FILE_PATH);
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'autospam_enabled' => (bool) config_cache('pixelfed.bouncer.enabled') ?? false,
|
|
|
+ 'nlp_enabled' => (bool) AutospamService::active(),
|
|
|
+ 'files' => $files,
|
|
|
+ 'open' => $open,
|
|
|
+ 'closed' => $closed,
|
|
|
+ 'graph' => collect($thisWeek)->map(fn($s) => $s['y'])->values(),
|
|
|
+ 'graphLabels' => collect($thisWeek)->map(fn($s) => $s['x'])->values()
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getAutospamReportsClosedApi(Request $request)
|
|
|
+ {
|
|
|
+ $appeals = AdminSpamReport::collection(
|
|
|
+ AccountInterstitial::orderBy('id', 'desc')
|
|
|
+ ->whereType('post.autospam')
|
|
|
+ ->whereIsSpam(true)
|
|
|
+ ->whereNotNull('appeal_handled_at')
|
|
|
+ ->cursorPaginate(6)
|
|
|
+ ->withQueryString()
|
|
|
+ );
|
|
|
+
|
|
|
+ return $appeals;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function postAutospamTrainSpamApi(Request $request)
|
|
|
+ {
|
|
|
+ $aiCount = AccountInterstitial::whereItemType('App\Status')
|
|
|
+ ->whereIsSpam(true)
|
|
|
+ ->count();
|
|
|
+ abort_if($aiCount < 100, 422, 'You don\'t have enough data to pre-train against.');
|
|
|
+
|
|
|
+ $existing = Cache::get('pf:admin:autospam:pretrain:recent');
|
|
|
+ abort_if($existing, 422, 'You\'ve already run this recently, please wait 30 minutes before pre-training again');
|
|
|
+ AutospamPretrainPipeline::dispatch();
|
|
|
+ Cache::put('pf:admin:autospam:pretrain:recent', 1, 1440);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'msg' => 'Success!'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function postAutospamTrainNonSpamSearchApi(Request $request)
|
|
|
+ {
|
|
|
+ $this->validate($request, [
|
|
|
+ 'q' => 'required|string|min:1'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $q = $request->input('q');
|
|
|
+
|
|
|
+ $res = Profile::whereNull(['status', 'domain'])
|
|
|
+ ->where('username', 'like', '%' . $q . '%')
|
|
|
+ ->orderByDesc('followers_count')
|
|
|
+ ->take(10)
|
|
|
+ ->get()
|
|
|
+ ->map(function($p) {
|
|
|
+ $acct = AccountService::get($p->id, true);
|
|
|
+ return [
|
|
|
+ 'id' => (string) $p->id,
|
|
|
+ 'avatar' => $acct['avatar'],
|
|
|
+ 'username' => $p->username
|
|
|
+ ];
|
|
|
+ })
|
|
|
+ ->values();
|
|
|
+ return $res;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function postAutospamTrainNonSpamSubmitApi(Request $request)
|
|
|
+ {
|
|
|
+ $this->validate($request, [
|
|
|
+ 'accounts' => 'required|array|min:1|max:10'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $accts = $request->input('accounts');
|
|
|
+
|
|
|
+ $accounts = Profile::whereNull(['domain', 'status'])->find(collect($accts)->map(function($a) { return $a['id'];}));
|
|
|
+
|
|
|
+ abort_if(!$accounts || !$accounts->count(), 422, 'One or more of the selected accounts are not valid');
|
|
|
+
|
|
|
+ AutospamPretrainNonSpamPipeline::dispatch($accounts);
|
|
|
+ return $accounts;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getAutospamCustomTokensApi(Request $request)
|
|
|
+ {
|
|
|
+ return AutospamCustomTokens::latest()->cursorPaginate(6);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function saveNewAutospamCustomTokensApi(Request $request)
|
|
|
+ {
|
|
|
+ $this->validate($request, [
|
|
|
+ 'token' => 'required|unique:autospam_custom_tokens,token',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $ct = new AutospamCustomTokens;
|
|
|
+ $ct->token = $request->input('token');
|
|
|
+ $ct->weight = $request->input('weight');
|
|
|
+ $ct->category = $request->input('category') === 'spam' ? 'spam' : 'ham';
|
|
|
+ $ct->note = $request->input('note');
|
|
|
+ $ct->active = $request->input('active');
|
|
|
+ $ct->save();
|
|
|
+
|
|
|
+ AutospamUpdateCachedDataPipeline::dispatch();
|
|
|
+ return $ct;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function updateAutospamCustomTokensApi(Request $request)
|
|
|
+ {
|
|
|
+ $this->validate($request, [
|
|
|
+ 'id' => 'required',
|
|
|
+ 'token' => 'required',
|
|
|
+ 'category' => 'required|in:spam,ham',
|
|
|
+ 'active' => 'required|boolean'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $ct = AutospamCustomTokens::findOrFail($request->input('id'));
|
|
|
+ $ct->weight = $request->input('weight');
|
|
|
+ $ct->category = $request->input('category');
|
|
|
+ $ct->note = $request->input('note');
|
|
|
+ $ct->active = $request->input('active');
|
|
|
+ $ct->save();
|
|
|
+
|
|
|
+ AutospamUpdateCachedDataPipeline::dispatch();
|
|
|
+
|
|
|
+ return $ct;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function exportAutospamCustomTokensApi(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!Storage::exists(AutospamService::MODEL_SPAM_PATH), 422, 'Autospam Dataset does not exist, please train spam before attempting to export');
|
|
|
+ return Storage::download(AutospamService::MODEL_SPAM_PATH);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function enableAutospamApi(Request $request)
|
|
|
+ {
|
|
|
+ ConfigCacheService::put('autospam.nlp.enabled', true);
|
|
|
+ Cache::forget(AutospamService::CHCKD_CACHE_KEY);
|
|
|
+ return ['msg' => 'Success'];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function disableAutospamApi(Request $request)
|
|
|
+ {
|
|
|
+ ConfigCacheService::put('autospam.nlp.enabled', false);
|
|
|
+ Cache::forget(AutospamService::CHCKD_CACHE_KEY);
|
|
|
+ return ['msg' => 'Success'];
|
|
|
+ }
|
|
|
+}
|