Fix nickname and nsfw pfp scanning

pull/41/head
Skyler Grey 3 years ago
parent 5b78b42db8
commit 144327101d
Signed by: Minion3665
GPG Key ID: 1AFD10256B3C714D

@ -21,7 +21,7 @@
"mongodb": "^4.7.0", "mongodb": "^4.7.0",
"node-fetch": "^3.3.0", "node-fetch": "^3.3.0",
"node-tesseract-ocr": "^2.2.1", "node-tesseract-ocr": "^2.2.1",
"nsfwjs": "2.4.2", "nsfwjs": "https://github.com/infinitered/nsfwjs",
"seedrandom": "^3.0.5", "seedrandom": "^3.0.5",
"structured-clone": "^0.2.2", "structured-clone": "^0.2.2",
"systeminformation": "^5.17.3" "systeminformation": "^5.17.3"

@ -2,7 +2,7 @@ import { getCommandMentionByName } from "./../utils/getCommandDataByName.js";
import client from "../utils/client.js"; import client from "../utils/client.js";
import keyValueList from "../utils/generateKeyValueList.js"; import keyValueList from "../utils/generateKeyValueList.js";
import singleNotify from "../utils/singleNotify.js"; import singleNotify from "../utils/singleNotify.js";
import { saveAttachment } from "../reflex/scanners.js"; import { streamAttachment } from "../reflex/scanners.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import addPlural from "../utils/plurals.js"; import addPlural from "../utils/plurals.js";
import type { GuildTextBasedChannel, Message } from "discord.js"; import type { GuildTextBasedChannel, Message } from "discord.js";
@ -14,7 +14,7 @@ export default async function logAttachment(message: Message): Promise<Attachmen
const attachments = []; const attachments = [];
for (const attachment of message.attachments.values()) { for (const attachment of message.attachments.values()) {
attachments.push({ attachments.push({
local: (await saveAttachment(attachment.url))[0], local: (await streamAttachment(attachment.url))[0],
url: attachment.url, url: attachment.url,
height: attachment.height, height: attachment.height,
width: attachment.width, width: attachment.width,
@ -25,7 +25,7 @@ export default async function logAttachment(message: Message): Promise<Attachmen
for (const link of links) { for (const link of links) {
if (link.toLowerCase().match(/\.(jpg|jpeg|png|gif|gifv|webm|webp|mp4|wav|mp3|ogg)$/gi)) { if (link.toLowerCase().match(/\.(jpg|jpeg|png|gif|gifv|webm|webp|mp4|wav|mp3|ogg)$/gi)) {
attachments.push({ attachments.push({
local: (await saveAttachment(link))[0], local: (await streamAttachment(link))[0],
url: link, url: link,
height: null, height: null,
width: null width: null

@ -5,9 +5,9 @@ import Tesseract from "node-tesseract-ocr";
import type Discord from "discord.js"; import type Discord from "discord.js";
import client from "../utils/client.js"; import client from "../utils/client.js";
import { createHash } from "crypto"; import { createHash } from "crypto";
// import * as nsfwjs from "nsfwjs"; import * as nsfwjs from "nsfwjs/src";
// import * as clamscan from "clamscan"; import * as clamscan from "clamscan";
// import * as tf from "@tensorflow/tfjs-node"; import * as tf from "@tensorflow/tfjs-node";
import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import getEmojiByName from "../utils/getEmojiByName.js"; import getEmojiByName from "../utils/getEmojiByName.js";
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
@ -21,26 +21,28 @@ interface MalwareSchema {
errored?: boolean; errored?: boolean;
} }
// const model = await nsfwjs.load(); const nsfw_model = await nsfwjs.load();
export async function testNSFW(link: string): Promise<NSFWSchema> { export async function testNSFW(link: string): Promise<NSFWSchema> {
const [_fileName, hash] = await saveAttachment(link); const [fileStream, hash] = await streamAttachment(link);
const alreadyHaveCheck = await client.database.scanCache.read(hash); const alreadyHaveCheck = await client.database.scanCache.read(hash);
if (alreadyHaveCheck) return { nsfw: alreadyHaveCheck.data }; if (alreadyHaveCheck?.nsfw) return { nsfw: alreadyHaveCheck.nsfw };
// const image = tf.node.decodePng() const image = tf.node.decodeImage(new Uint8Array(fileStream), 3) as tf.Tensor3D;
// const result = await model.classify(image) const predictions = (await nsfw_model.classify(image, 1))[0]!;
image.dispose();
return { nsfw: false }; return { nsfw: predictions.className === "Hentai" || predictions.className === "Porn" };
} }
export async function testMalware(link: string): Promise<MalwareSchema> { export async function testMalware(link: string): Promise<MalwareSchema> {
const [p, hash] = await saveAttachment(link); const [p, hash] = await streamAttachment(link);
const alreadyHaveCheck = await client.database.scanCache.read(hash); const alreadyHaveCheck = await client.database.scanCache.read(hash);
if (alreadyHaveCheck) return { safe: alreadyHaveCheck.data }; if (alreadyHaveCheck?.malware) return { safe: alreadyHaveCheck.malware };
return { safe: true };
const data = new URLSearchParams(); const data = new URLSearchParams();
const f = createReadStream(p); // const f = createReadStream(p);
data.append("file", f.read(fs.statSync(p).size)); data.append("file", f.read(fs.statSync(p).size));
const result = await fetch("https://unscan.p.rapidapi.com/malware", { const result = await fetch("https://unscan.p.rapidapi.com/malware", {
method: "POST", method: "POST",
@ -58,14 +60,14 @@ export async function testMalware(link: string): Promise<MalwareSchema> {
return { safe: true, errored: true }; return { safe: true, errored: true };
}); });
if (!result.errored) { if (!result.errored) {
client.database.scanCache.write(hash, result.safe); client.database.scanCache.write(hash, "malware", result.safe);
} }
return { safe: result.safe }; return { safe: result.safe };
} }
export async function testLink(link: string): Promise<{ safe: boolean; tags: string[] }> { export async function testLink(link: string): Promise<{ safe: boolean; tags: string[] }> {
const alreadyHaveCheck = await client.database.scanCache.read(link); const alreadyHaveCheck = await client.database.scanCache.read(link);
if (alreadyHaveCheck) return { safe: alreadyHaveCheck.data, tags: [] }; if (alreadyHaveCheck?.bad_link) return { safe: alreadyHaveCheck.bad_link, tags: alreadyHaveCheck.tags };
const scanned: { safe?: boolean; tags?: string[] } = await fetch("https://unscan.p.rapidapi.com/link", { const scanned: { safe?: boolean; tags?: string[] } = await fetch("https://unscan.p.rapidapi.com/link", {
method: "POST", method: "POST",
headers: { headers: {
@ -79,19 +81,19 @@ export async function testLink(link: string): Promise<{ safe: boolean; tags: str
console.error(err); console.error(err);
return { safe: true, tags: [] }; return { safe: true, tags: [] };
}); });
client.database.scanCache.write(link, scanned.safe ?? true, []); client.database.scanCache.write(link, "bad_link", scanned.safe ?? true, scanned.tags ?? []);
return { return {
safe: scanned.safe ?? true, safe: scanned.safe ?? true,
tags: scanned.tags ?? [] tags: scanned.tags ?? []
}; };
} }
export async function saveAttachment(link: string): Promise<[string, string]> { export async function streamAttachment(link: string): Promise<[ArrayBuffer, string]> {
const image = await (await fetch(link)).arrayBuffer(); const image = await (await fetch(link)).arrayBuffer();
const fileName = generateFileName(link.split("/").pop()!.split(".").pop()!); const fileName = generateFileName(link.split("/").pop()!.split(".").pop()!);
const enc = new TextDecoder("utf-8"); const enc = new TextDecoder("utf-8");
writeFileSync(fileName, new DataView(image), "base64"); writeFileSync(fileName, new DataView(image), "base64");
return [fileName, createHash("sha512").update(enc.decode(image), "base64").digest("base64")]; return [image, createHash("sha512").update(enc.decode(image), "base64").digest("base64")];
} }
const linkTypes = { const linkTypes = {
@ -218,11 +220,10 @@ export async function doMemberChecks(member: Discord.GuildMember, guild: Discord
const avatarCheck = const avatarCheck =
guildData.filters.images.NSFW && (await NSFWCheck(member.user.displayAvatarURL({ forceStatic: true }))); guildData.filters.images.NSFW && (await NSFWCheck(member.user.displayAvatarURL({ forceStatic: true })));
// Does the username contain an invite // Does the username contain an invite
const inviteCheck = const inviteCheck = guildData.filters.invite.enabled && /discord\.gg\/[a-zA-Z0-9]+/gi.test(member.user.username);
guildData.filters.invite.enabled && member.user.username.match(/discord\.gg\/[a-zA-Z0-9]+/gi) !== null;
// Does the nickname contain an invite // Does the nickname contain an invite
const nicknameInviteCheck = const nicknameInviteCheck =
guildData.filters.invite.enabled && member.nickname?.match(/discord\.gg\/[a-zA-Z0-9]+/gi) !== null; guildData.filters.invite.enabled && /discord\.gg\/[a-zA-Z0-9]+/gi.test(member.nickname ?? "");
if ( if (
usernameCheck !== null || usernameCheck !== null ||

@ -588,7 +588,9 @@ export class History {
interface ScanCacheSchema { interface ScanCacheSchema {
addedAt: Date; addedAt: Date;
hash: string; hash: string;
data: boolean; nsfw?: boolean;
malware?: boolean;
bad_link?: boolean;
tags: string[]; tags: string[];
} }
@ -600,14 +602,12 @@ export class ScanCache {
} }
async read(hash: string) { async read(hash: string) {
// console.log("ScanCache read");
return await this.scanCache.findOne({ hash: hash }); return await this.scanCache.findOne({ hash: hash });
} }
async write(hash: string, data: boolean, tags?: string[]) { async write(hash: string, type: "nsfw" | "malware" | "bad_link", data: boolean, tags?: string[]) {
// console.log("ScanCache write");
await this.scanCache.insertOne( await this.scanCache.insertOne(
{ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() }, { hash: hash, [type]: data, tags: tags ?? [], addedAt: new Date() },
collectionOptions collectionOptions
); );
} }

Loading…
Cancel
Save