Add comment spam classification and captcha checks.
Score artwork comments with local signatures plus Together AI, and optionally require Turnstile before posting.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Moderation;
|
||||
|
||||
use App\Contracts\Moderation\CommentSpamClassifier;
|
||||
use App\Data\Moderation\CommentSpamClassification;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
final class TogetherCommentSpamClassifier implements CommentSpamClassifier
|
||||
{
|
||||
public function classify(string $content): CommentSpamClassification
|
||||
{
|
||||
$config = (array) config('comment_spam.ai', []);
|
||||
$key = (string) ($config['api_key'] ?? '');
|
||||
$model = (string) ($config['model'] ?? '');
|
||||
if ($key === '' || $model === '') {
|
||||
throw new RuntimeException('Together AI is not configured.');
|
||||
}
|
||||
if (! in_array($model, (array) ($config['allowed_models'] ?? []), true)) {
|
||||
throw new RuntimeException('Unsupported Together AI comment spam model.');
|
||||
}
|
||||
|
||||
$started = microtime(true);
|
||||
$response = Http::timeout(min(5, max(1, (int) ($config['timeout'] ?? 5))))
|
||||
->withToken($key)
|
||||
->post(rtrim((string) ($config['base_url'] ?? 'https://api.together.xyz/v1'), '/').'/chat/completions', [
|
||||
'model' => $model,
|
||||
'temperature' => 0,
|
||||
'response_format' => ['type' => 'json_object'],
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => 'Classify the comment as spam or not spam. Return only JSON: {"spam":true|false,"confidence":0.0,"reason":"short explanation"}. Confidence must be a number from 0 to 1. Spam includes advertising, SEO promotion, unsolicited links, scams, and bot-like promotional repetition.'],
|
||||
['role' => 'user', 'content' => mb_substr($content, 0, (int) ($config['max_input_chars'] ?? 2500))],
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->failed()) {
|
||||
throw new RuntimeException('Together AI request failed with status '.$response->status().'.');
|
||||
}
|
||||
|
||||
$message = Arr::get($response->json(), 'choices.0.message.content');
|
||||
$decoded = is_string($message) ? json_decode($message, true) : null;
|
||||
if (! is_array($decoded) || ! is_bool($decoded['spam'] ?? null)
|
||||
|| ! is_numeric($decoded['confidence'] ?? null)
|
||||
|| $decoded['confidence'] < 0 || $decoded['confidence'] > 1
|
||||
|| ! is_string($decoded['reason'] ?? null)) {
|
||||
throw new RuntimeException('Together AI returned invalid moderation JSON.');
|
||||
}
|
||||
|
||||
return new CommentSpamClassification(
|
||||
$decoded['spam'],
|
||||
(float) $decoded['confidence'],
|
||||
mb_substr(trim($decoded['reason']), 0, 500),
|
||||
'together',
|
||||
$model,
|
||||
(int) round((microtime(true) - $started) * 1000),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user