feat: profanity censoring and filtering
This commit is contained in:
parent
e1d248853f
commit
25c9bb079c
9 changed files with 41 additions and 5 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
"postinstall": "prisma generate"
|
"postinstall": "prisma generate"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@2toad/profanity": "^3.1.1",
|
||||||
"@auth/prisma-adapter": "2.7.2",
|
"@auth/prisma-adapter": "2.7.2",
|
||||||
"@bprogress/next": "^3.2.11",
|
"@bprogress/next": "^3.2.11",
|
||||||
"@hello-pangea/dnd": "^18.0.1",
|
"@hello-pangea/dnd": "^18.0.1",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@2toad/profanity':
|
||||||
|
specifier: ^3.1.1
|
||||||
|
version: 3.1.1
|
||||||
'@auth/prisma-adapter':
|
'@auth/prisma-adapter':
|
||||||
specifier: 2.7.2
|
specifier: 2.7.2
|
||||||
version: 2.7.2(@prisma/client@6.6.0(prisma@6.6.0(typescript@5.8.3))(typescript@5.8.3))
|
version: 2.7.2(@prisma/client@6.6.0(prisma@6.6.0(typescript@5.8.3))(typescript@5.8.3))
|
||||||
|
|
@ -108,6 +111,10 @@ importers:
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@2toad/profanity@3.1.1':
|
||||||
|
resolution: {integrity: sha512-07ny4pCSa4gDrcJ4vZ/WWmiM90+8kv/clXfnDvThf9IJq0GldpjRVdzHCfMwGDs2Y/8eClmTGzKb5tEfUWy/uA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0':
|
'@alloc/quick-lru@5.2.0':
|
||||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -2301,6 +2308,8 @@ packages:
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@2toad/profanity@3.1.1': {}
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0': {}
|
'@alloc/quick-lru@5.2.0': {}
|
||||||
|
|
||||||
'@auth/core@0.37.2':
|
'@auth/core@0.37.2':
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { profanity } from "@2toad/profanity";
|
||||||
|
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
@ -14,6 +15,9 @@ export async function PATCH(request: NextRequest) {
|
||||||
const validation = displayNameSchema.safeParse(displayName);
|
const validation = displayNameSchema.safeParse(displayName);
|
||||||
if (!validation.success) return NextResponse.json({ error: validation.error.errors[0].message }, { status: 400 });
|
if (!validation.success) return NextResponse.json({ error: validation.error.errors[0].message }, { status: 400 });
|
||||||
|
|
||||||
|
// Check for inappropriate words
|
||||||
|
if (profanity.exists(displayName)) return NextResponse.json({ error: "Display name contains inappropriate words" }, { status: 400 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: { email: session.user?.email ?? undefined },
|
where: { email: session.user?.email ?? undefined },
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { profanity } from "@2toad/profanity";
|
||||||
|
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { usernameSchema } from "@/lib/schemas";
|
import { usernameSchema } from "@/lib/schemas";
|
||||||
import dayjs from "dayjs";
|
|
||||||
|
|
||||||
export async function PATCH(request: NextRequest) {
|
export async function PATCH(request: NextRequest) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
@ -24,6 +26,9 @@ export async function PATCH(request: NextRequest) {
|
||||||
const validation = usernameSchema.safeParse(username);
|
const validation = usernameSchema.safeParse(username);
|
||||||
if (!validation.success) return NextResponse.json({ error: validation.error.errors[0].message }, { status: 400 });
|
if (!validation.success) return NextResponse.json({ error: validation.error.errors[0].message }, { status: 400 });
|
||||||
|
|
||||||
|
// Check for inappropriate words
|
||||||
|
if (profanity.exists(username)) return NextResponse.json({ error: "Username contains inappropriate words" }, { status: 400 });
|
||||||
|
|
||||||
const existingUser = await prisma.user.findUnique({ where: { username } });
|
const existingUser = await prisma.user.findUnique({ where: { username } });
|
||||||
if (existingUser) return NextResponse.json({ error: "Username is already taken" }, { status: 400 });
|
if (existingUser) return NextResponse.json({ error: "Username is already taken" }, { status: 400 });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
|
||||||
|
import { profanity } from "@2toad/profanity";
|
||||||
|
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { idSchema, nameSchema, tagsSchema } from "@/lib/schemas";
|
import { idSchema, nameSchema, tagsSchema } from "@/lib/schemas";
|
||||||
|
|
@ -81,8 +83,8 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
|
||||||
|
|
||||||
// Edit Mii in database
|
// Edit Mii in database
|
||||||
const updateData: Partial<Mii> = {};
|
const updateData: Partial<Mii> = {};
|
||||||
if (name !== undefined) updateData.name = name;
|
if (name !== undefined) updateData.name = profanity.censor(name); // Censor potential inappropriate words
|
||||||
if (tags !== undefined) updateData.tags = tags;
|
if (tags !== undefined) updateData.tags = tags.map((t) => profanity.censor(t)); // Same here
|
||||||
if (images.length > 0) updateData.imageCount = images.length;
|
if (images.length > 0) updateData.imageCount = images.length;
|
||||||
|
|
||||||
if (Object.keys(updateData).length == 0) return NextResponse.json({ error: "Nothing was changed" }, { status: 400 });
|
if (Object.keys(updateData).length == 0) return NextResponse.json({ error: "Nothing was changed" }, { status: 400 });
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import path from "path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
|
||||||
import qrcode from "qrcode-generator";
|
import qrcode from "qrcode-generator";
|
||||||
|
import { profanity } from "@2toad/profanity";
|
||||||
|
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
@ -54,7 +55,11 @@ export async function POST(request: Request) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parsed.success) return NextResponse.json({ error: parsed.error.errors[0].message }, { status: 400 });
|
if (!parsed.success) return NextResponse.json({ error: parsed.error.errors[0].message }, { status: 400 });
|
||||||
const { name, tags, qrBytesRaw, image1, image2, image3 } = parsed.data;
|
const { name: uncensoredName, tags: uncensoredTags, qrBytesRaw, image1, image2, image3 } = parsed.data;
|
||||||
|
|
||||||
|
// Censor potential inappropriate words
|
||||||
|
const name = profanity.censor(uncensoredName);
|
||||||
|
const tags = uncensoredTags.map((t) => profanity.censor(t));
|
||||||
|
|
||||||
// Validate image files
|
// Validate image files
|
||||||
const images: File[] = [];
|
const images: File[] = [];
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ export default function PrivacyPage() {
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold">Terms of Service</h1>
|
<h1 className="text-2xl font-bold">Terms of Service</h1>
|
||||||
<h2 className="font-light">
|
<h2 className="font-light">
|
||||||
<strong className="font-medium">Effective Date:</strong> April 06, 2025
|
<strong className="font-medium">Effective Date:</strong> April 23, 2025
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<hr className="border-black/20 mt-1 mb-4" />
|
<hr className="border-black/20 mt-1 mb-4" />
|
||||||
|
|
@ -34,6 +34,7 @@ export default function PrivacyPage() {
|
||||||
<li>No impersonation of others.</li>
|
<li>No impersonation of others.</li>
|
||||||
<li>No malware, malicious links, or phishing content.</li>
|
<li>No malware, malicious links, or phishing content.</li>
|
||||||
<li>No harassment, hate speech, threats, or bullying towards others.</li>
|
<li>No harassment, hate speech, threats, or bullying towards others.</li>
|
||||||
|
<li>Avoid using inappropriate language. Profanity may be automatically censored.</li>
|
||||||
<li>No use of automated scripts, bots, or scrapers to access or interact with the site.</li>
|
<li>No use of automated scripts, bots, or scrapers to access or interact with the site.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { profanity } from "@2toad/profanity";
|
||||||
import { AES_CCM } from "@trafficlunar/asmcrypto.js";
|
import { AES_CCM } from "@trafficlunar/asmcrypto.js";
|
||||||
|
|
||||||
import { MII_DECRYPTION_KEY } from "./constants";
|
import { MII_DECRYPTION_KEY } from "./constants";
|
||||||
import Mii from "./mii.js/mii";
|
import Mii from "./mii.js/mii";
|
||||||
import TomodachiLifeMii from "./tomodachi-life-mii";
|
import TomodachiLifeMii from "./tomodachi-life-mii";
|
||||||
|
|
@ -39,6 +41,11 @@ export function convertQrCode(bytes: Uint8Array): { mii: Mii; tomodachiLifeMii:
|
||||||
mii.facialHairColor = tomodachiLifeMii.studioHairColor;
|
mii.facialHairColor = tomodachiLifeMii.studioHairColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Censor potential inappropriate words
|
||||||
|
tomodachiLifeMii.firstName = profanity.censor(tomodachiLifeMii.firstName);
|
||||||
|
tomodachiLifeMii.lastName = profanity.censor(tomodachiLifeMii.lastName);
|
||||||
|
tomodachiLifeMii.islandName = profanity.censor(tomodachiLifeMii.islandName);
|
||||||
|
|
||||||
return { mii, tomodachiLifeMii };
|
return { mii, tomodachiLifeMii };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// profanity censoring bypasses the regex in some of these but I think it's funny
|
||||||
|
|
||||||
export const querySchema = z
|
export const querySchema = z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue