增加 nsfwjs 图片审核驱动 Closed #347

This commit is contained in:
Wisp X 2022-03-24 10:07:25 +08:00
parent 6c31ace31a
commit 43bb7792ac
6 changed files with 89 additions and 2 deletions

View File

@ -0,0 +1,15 @@
<?php
namespace App\Enums\Scan;
final class NsfwJsOption
{
/** @var string 接口地址 */
const ApiUrl = 'api_url';
/** @var string 表单属性名称 */
const AttrName = 'attr_name';
/** @var string 阈值 */
const Threshold = 'threshold';
}

View File

@ -43,7 +43,7 @@ class GroupRequest extends FormRequest
'exclude_if:configs.is_enable_scan,false', 'exclude_if:configs.is_enable_scan,false',
'in:mark,delete', 'in:mark,delete',
], ],
'configs.scan_configs.driver' => ['exclude_if:configs.is_enable_scan,false', 'in:tencent,aliyun'], 'configs.scan_configs.driver' => ['exclude_if:configs.is_enable_scan,false', 'in:tencent,aliyun,nsfwjs'],
'configs.scan_configs.drivers.tencent.endpoint' => [$requiredIfReview('tencent')], 'configs.scan_configs.drivers.tencent.endpoint' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_id' => [$requiredIfReview('tencent')], 'configs.scan_configs.drivers.tencent.secret_id' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_key' => [$requiredIfReview('tencent')], 'configs.scan_configs.drivers.tencent.secret_key' => [$requiredIfReview('tencent')],
@ -56,6 +56,10 @@ class GroupRequest extends FormRequest
'configs.scan_configs.drivers.aliyun.biz_type' => '', 'configs.scan_configs.drivers.aliyun.biz_type' => '',
'configs.scan_configs.drivers.aliyun.scenes' => [$requiredIfReview('aliyun'), 'array'], 'configs.scan_configs.drivers.aliyun.scenes' => [$requiredIfReview('aliyun'), 'array'],
'configs.scan_configs.drivers.nsfwjs.api_url' => [$requiredIfReview('nsfwjs')],
'configs.scan_configs.drivers.nsfwjs.attr_name' => [$requiredIfReview('nsfwjs')],
'configs.scan_configs.drivers.nsfwjs.threshold' => [$requiredIfReview('nsfwjs'), 'min:1', 'max:100'],
'configs.is_enable_original_protection' => 'boolean', 'configs.is_enable_original_protection' => 'boolean',
'configs.image_cache_ttl' => 'nullable|numeric', 'configs.image_cache_ttl' => 'nullable|numeric',
@ -126,6 +130,9 @@ class GroupRequest extends FormRequest
'configs.scan_configs.drivers.aliyun.region_id' => '地域节点', 'configs.scan_configs.drivers.aliyun.region_id' => '地域节点',
'configs.scan_configs.drivers.aliyun.biz_type' => '场景名称', 'configs.scan_configs.drivers.aliyun.biz_type' => '场景名称',
'configs.scan_configs.drivers.aliyun.scenes' => '审核场景', 'configs.scan_configs.drivers.aliyun.scenes' => '审核场景',
'configs.scan_configs.drivers.nsfwjs.api_url' => '接口地址',
'configs.scan_configs.drivers.nsfwjs.attr_name' => '表单名称',
'configs.scan_configs.drivers.nsfwjs.threshold' => '阈值',
'configs.is_enable_original_protection' => '是否启用原图保护功能', 'configs.is_enable_original_protection' => '是否启用原图保护功能',
'configs.image_cache_ttl' => '图片缓存时间', 'configs.image_cache_ttl' => '图片缓存时间',

View File

@ -8,6 +8,7 @@ use App\Enums\ConfigKey;
use App\Enums\GroupConfigKey; use App\Enums\GroupConfigKey;
use App\Enums\ImagePermission; use App\Enums\ImagePermission;
use App\Enums\Scan\AliyunOption; use App\Enums\Scan\AliyunOption;
use App\Enums\Scan\NsfwJsOption;
use App\Enums\Scan\TencentOption; use App\Enums\Scan\TencentOption;
use App\Enums\Strategy\CosOption; use App\Enums\Strategy\CosOption;
use App\Enums\Strategy\FtpOption; use App\Enums\Strategy\FtpOption;
@ -38,6 +39,7 @@ use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Intervention\Image\Facades\Image as InterventionImage; use Intervention\Image\Facades\Image as InterventionImage;
use Intervention\Image\Imagick\Font; use Intervention\Image\Imagick\Font;
@ -429,6 +431,29 @@ class ImageService
} }
} }
} }
if ($driver === 'nsfwjs') {
// 不支持 bmp 格式
if ($image->extension === 'bmp') {
return false;
}
$response = Http::timeout(180)->withOptions(['timeout' => 180])->attach(
$configs->get(NsfwJsOption::AttrName), $file->getContent(), $file->getClientOriginalName(),
)->post($configs->get(NsfwJsOption::ApiUrl));
$ratio = $configs->get(NsfwJsOption::Threshold, 60) / 100;
if ($response->json('hentai', 0.00) >= $ratio) {
$flag = true;
}
if ($response->json('porn', 0.00) >= $ratio) {
$flag = true;
}
if ($response->json('sexy', 0.00) >= $ratio) {
$flag = true;
}
}
} catch (\Throwable $e) { } catch (\Throwable $e) {
throw new UploadException('Scan: '.$e->getMessage()); throw new UploadException('Scan: '.$e->getMessage());
} }

View File

@ -8,6 +8,7 @@ use App\Enums\ImagePermission;
use App\Enums\Mail\SmtpOption; use App\Enums\Mail\SmtpOption;
use App\Enums\PastedAction; use App\Enums\PastedAction;
use App\Enums\Scan\AliyunOption; use App\Enums\Scan\AliyunOption;
use App\Enums\Scan\NsfwJsOption;
use App\Enums\Scan\TencentOption; use App\Enums\Scan\TencentOption;
use App\Enums\UserConfigKey; use App\Enums\UserConfigKey;
use App\Enums\Watermark\FontOption; use App\Enums\Watermark\FontOption;
@ -66,6 +67,11 @@ return [
AliyunOption::Scenes => ['porn'], AliyunOption::Scenes => ['porn'],
AliyunOption::BizType => '', AliyunOption::BizType => '',
], ],
'nsfwjs' => [
NsfwJsOption::ApiUrl => '',
NsfwJsOption::AttrName => 'image',
NsfwJsOption::Threshold => 60,
]
], ],
], ],
GroupConfigKey::WatermarkConfigs => [ GroupConfigKey::WatermarkConfigs => [

View File

@ -118,6 +118,7 @@
<x-fieldset title="审核驱动"> <x-fieldset title="审核驱动">
<x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" checked>腾讯云</x-fieldset-radio> <x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" checked>腾讯云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]_aliyun" name="configs[scan_configs][driver]" data-select="scan" value="aliyun">阿里云</x-fieldset-radio> <x-fieldset-radio id="configs[scan_configs][driver]_aliyun" name="configs[scan_configs][driver]" data-select="scan" value="aliyun">阿里云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]_nsfwjs" name="configs[scan_configs][driver]" data-select="scan" value="nsfwjs">NsfwJs</x-fieldset-radio>
</x-fieldset> </x-fieldset>
</div> </div>
@ -169,6 +170,22 @@
</x-fieldset> </x-fieldset>
</div> </div>
</div> </div>
<div class="hidden mb-4" data-scan-driver="nsfwjs">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][api_url]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>接口地址</label>
<x-input type="url" name="configs[scan_configs][drivers][nsfwjs][api_url]" id="configs[scan_configs][drivers][nsfwjs][api_url]" autocomplete="api_url" placeholder="请输入接口地址http(s)://domain.com/classify" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][attr_name]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>属性名称</label>
<x-input type="text" name="configs[scan_configs][drivers][nsfwjs][attr_name]" id="configs[scan_configs][drivers][nsfwjs][attr_name]" autocomplete="attr_name" placeholder="接口的表单文件属性名" value="image" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][threshold]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>阈值</label>
<x-input type="number" name="configs[scan_configs][drivers][nsfwjs][threshold]" id="configs[scan_configs][drivers][nsfwjs][threshold]" autocomplete="threshold" placeholder="取值 1-100" value="60" />
<small class="text-gray-500"><i class="fas fa-exclamation-circle"></i> 阈值是指图片违规程度上限,取值 1-100 之间,数值越低审核越严格</small>
</div>
</div>
</div> </div>
<div data-tab="protection" class="hidden grid grid-cols-6 gap-6"> <div data-tab="protection" class="hidden grid grid-cols-6 gap-6">

View File

@ -113,7 +113,8 @@
<div class="col-span-6 mb-4"> <div class="col-span-6 mb-4">
<x-fieldset title="审核驱动"> <x-fieldset title="审核驱动">
<x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'tencent'">腾讯云</x-fieldset-radio> <x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'tencent'">腾讯云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]" name="configs[scan_configs][driver]" data-select="scan" value="aliyun" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'aliyun'">阿里云</x-fieldset-radio> <x-fieldset-radio id="configs[scan_configs][driver]_aliyun" name="configs[scan_configs][driver]" data-select="scan" value="aliyun" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'aliyun'">阿里云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]_nsfwjs" name="configs[scan_configs][driver]" data-select="scan" value="nsfwjs" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'nsfwjs'">NsfwJs</x-fieldset-radio>
</x-fieldset> </x-fieldset>
</div> </div>
@ -165,6 +166,22 @@
</x-fieldset> </x-fieldset>
</div> </div>
</div> </div>
<div class="hidden mb-4" data-scan-driver="nsfwjs">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][api_url]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>接口地址</label>
<x-input type="url" name="configs[scan_configs][drivers][nsfwjs][api_url]" id="configs[scan_configs][drivers][nsfwjs][api_url]" autocomplete="api_url" placeholder="请输入接口地址http(s)://domain.com/classify" value="{{ $group->configs['scan_configs']['drivers']['nsfwjs']['api_url'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][attr_name]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>属性名称</label>
<x-input type="text" name="configs[scan_configs][drivers][nsfwjs][attr_name]" id="configs[scan_configs][drivers][nsfwjs][attr_name]" autocomplete="attr_name" placeholder="接口的表单文件属性名" value="{{ $group->configs['scan_configs']['drivers']['nsfwjs']['attr_name'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][nsfwjs][threshold]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>阈值</label>
<x-input type="number" name="configs[scan_configs][drivers][nsfwjs][threshold]" id="configs[scan_configs][drivers][nsfwjs][threshold]" autocomplete="threshold" placeholder="取值 1-100" value="{{ $group->configs['scan_configs']['drivers']['nsfwjs']['threshold'] ?? '' }}" />
<small class="text-gray-500"><i class="fas fa-exclamation-circle"></i> 阈值是指图片违规程度上限,取值 1-100 之间,数值越低审核越严格</small>
</div>
</div>
</div> </div>
<div data-tab="protection" class="hidden grid grid-cols-6 gap-6"> <div data-tab="protection" class="hidden grid grid-cols-6 gap-6">