Skip to content

Commit

Permalink
Automod review system
Browse files Browse the repository at this point in the history
  • Loading branch information
bubner committed Aug 20, 2023
1 parent eed4d8c commit 4cc0b80
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/Firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export interface MessageData {
muid: string; // Primary key
createdAt: number;
autoMod: boolean;
reviewed: null | string;
autoModProb: number;
displayName: string;
email: string;
Expand Down
10 changes: 9 additions & 1 deletion src/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ function Chat() {
});
}, []);

const [adminPerms, setAdminPerms] = useState(false);
useEffect(() => {
getData("users", toCommas(auth.currentUser?.email!)).then((userData) => {
if (!userData) return false;
setAdminPerms(userData.admin);
});
}, []);

const lastSeenTimestampRef = useRef(Date.now());
const [newMessage, setNewMessage] = useState(false);

Expand Down Expand Up @@ -182,7 +190,7 @@ function Chat() {
Object.keys(messages).length > 0 &&
Object.entries(messages)
.slice(paginationIndex * -PAGINATION_LIMIT)
.map(([muid, msg]) => <Message message={msg} key={muid} />)}
.map(([muid, msg]) => <Message isAdmin={adminPerms} message={msg} key={muid} />)}
{/* Dummy element for fluid interface */}
<div id="dummy" ref={dummy}></div>
<br /> <br /> <br />
Expand Down
71 changes: 64 additions & 7 deletions src/chat/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { useMemo, useState } from "react";
import { auth, MessageData } from "../Firebase";
import { auth, MessageData, updateMsg } from "../Firebase";
import Msgman from "./Msgman";
import "../css/App.css";
import "../css/Message.css";
Expand All @@ -24,8 +24,9 @@ export const getFileURL = (fileURL: string) => {

const filter = new Filter({ placeHolder: "♥" });

function Message(props: { message: MessageData; key: string }) {
function Message(props: { isAdmin: boolean; message: MessageData; key: string }) {
const { message } = props;
const isAdmin = props.isAdmin;
const [isHovering, setIsHovering] = useState(false);
const handleMouseOver = () => setIsHovering(true);
const handleMouseOut = () => setIsHovering(false);
Expand All @@ -51,10 +52,28 @@ function Message(props: { message: MessageData; key: string }) {
timestamp = new Date(Date.now());
}

function allow() {
if (!isAdmin || !window.confirm("Allow message?")) return;
updateMsg(message.id, {
reviewed: auth.currentUser?.uid,
autoMod: false,
});
}

async function deny() {
if (!isAdmin || !window.confirm("Retract message?")) return;
await updateMsg(message.id, {
reviewed: auth.currentUser?.uid,
isRetracted: true,
});
}

return (
// Determine whether the message was sent or received by checking the author and current user
<div
className={`message ${auth.currentUser?.uid === message.uid ? "sent" : "received"}${message.photoURL === "sys" ? " sys" : ""}`}
className={`message ${auth.currentUser?.uid === message.uid ? "sent" : "received"}${
message.photoURL === "sys" ? " sys" : ""
}`}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
>
Expand All @@ -64,7 +83,8 @@ function Message(props: { message: MessageData; key: string }) {
className="pfp"
src={message.photoURL}
onError={() => {
(document.getElementsByClassName("pfp")[0] as HTMLImageElement).src = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjg4IiBoZWlnaHQ9IjI4OCIgdmlld0JveD0iMCAwIDI4OCAyODgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyODgiIGhlaWdodD0iMjg4IiBmaWxsPSIjRTlFOUU5Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjIyIDEwOUMyMjIgMTM5LjM2NiAyMDQuNjQ3IDE2NS42OCAxNzkuMzE3IDE3OC41NjVDMjIzLjk4MSAxODcuNzE4IDI2Mi40NDMgMjEzLjg4NiAyODcuNjMzIDI1MEgyODhWMjg4SDBWMjUwSDAuMzY3MTg4QzI1LjU1NzQgMjEzLjg4NiA2NC4wMTkzIDE4Ny43MTggMTA4LjY4MyAxNzguNTY1QzgzLjM1MjggMTY1LjY4IDY2IDEzOS4zNjYgNjYgMTA5QzY2IDY1LjkyMTkgMTAwLjkyMiAzMSAxNDQgMzFDMTg3LjA3OCAzMSAyMjIgNjUuOTIxOSAyMjIgMTA5WiIgZmlsbD0iIzAwMDAwMCIvPgo8L3N2Zz4K";
(document.getElementsByClassName("pfp")[0] as HTMLImageElement).src =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjg4IiBoZWlnaHQ9IjI4OCIgdmlld0JveD0iMCAwIDI4OCAyODgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyODgiIGhlaWdodD0iMjg4IiBmaWxsPSIjRTlFOUU5Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjIyIDEwOUMyMjIgMTM5LjM2NiAyMDQuNjQ3IDE2NS42OCAxNzkuMzE3IDE3OC41NjVDMjIzLjk4MSAxODcuNzE4IDI2Mi40NDMgMjEzLjg4NiAyODcuNjMzIDI1MEgyODhWMjg4SDBWMjUwSDAuMzY3MTg4QzI1LjU1NzQgMjEzLjg4NiA2NC4wMTkzIDE4Ny43MTggMTA4LjY4MyAxNzguNTY1QzgzLjM1MjggMTY1LjY4IDY2IDEzOS4zNjYgNjYgMTA5QzY2IDY1LjkyMTkgMTAwLjkyMiAzMSAxNDQgMzFDMTg3LjA3OCAzMSAyMjIgNjUuOTIxOSAyMjIgMTA5WiIgZmlsbD0iIzAwMDAwMCIvPgo8L3N2Zz4K";
}}
alt={`Profile of ${message.displayName}`}
referrerPolicy="no-referrer"
Expand All @@ -77,7 +97,10 @@ function Message(props: { message: MessageData; key: string }) {

{/* Display the proper formatted date and time metadata with each message */}
{message.photoURL !== "sys" && (
<p className="date">{timestamp.toLocaleString("en-AU", { hour12: true })}</p>
<p className="date">
{timestamp.toLocaleString("en-AU", { hour12: true })} {message.autoMod ? "[AutoMod]" : ""}{" "}
{message.reviewed ? "[reviewed]" : ""}
</p>
)}
</div>

Expand All @@ -91,13 +114,47 @@ function Message(props: { message: MessageData; key: string }) {
<ReactMarkdown className="text" remarkPlugins={[gfm]} linkTarget="_blank">
{messageText}
</ReactMarkdown>
) : isAdmin ? (
<p className="text automod">
<h4>AutoMod flagged.</h4>
Message content requires a review and is currently only visible to administrators.{" "}
<br /> <br />
<strong>ACTIONS</strong>{" "}
<button className="allow" onClick={allow}>
Allow
</button>{" "}
<button className="deny" onClick={deny}>
Retract
</button>{" "}
<br />
<i>Profanity score: {message.autoModProb.toFixed(2)}</i> <br /> <br />
<b>Message content:</b> <br />
{message.text}
</p>
) : message.uid === auth.currentUser?.uid ? (
<>
<p className="text">
<div className="waiting">
<i>
Your message content is currently not visible as it has not been reviewed.
<br />
Please wait for an admin to review it.
</i>
<br />
</div>
{message.text}
</p>
</>
) : (
<p className="text"><strong>(MESSAGE AUTOMODDED)</strong> {messageText}</p>
<>
<p className="text">
<i>&lt;under review&gt;</i>
</p>
</>
)
) : (
<p className="text">{messageText}</p>
)
// TODO: Admin review automod system, flagging is up and running but we need to implement a system to handle them better than just saying (MESSAGE AUTOMODDED)
) : (
// Otherwise, it must be a file and we can display the downloadURL depending on it's type
// The type for the URL is prepended to the downloadURL with a colon
Expand Down
2 changes: 1 addition & 1 deletion src/chat/Msgman.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function Msgman({ id, isActive }: { id: string; isActive: boolean }) {
function viewData() {
if (!message) return;
// prettier-ignore
alert(`Message author: ${message.displayName}\nAuthor email: ${message.email}\nAuthor UID: ${message.uid}\nMessage ID: ${message.id}\nMessage creation time: ${message.createdAt}\nMessage type: ${message.isMsg ? "text" : "file"}\nMessage retracted? ${message.isRetracted ? "yes" : "no"}\nAutoMod profanity probability: ${message.autoModProb || "n/a"}\n\nMessage content:\n${message.text}`);
alert(`Message author: ${message.displayName}\nAuthor email: ${message.email}\nAuthor UID: ${message.uid}\nMessage ID: ${message.id}\nMessage creation time: ${message.createdAt}\nMessage type: ${message.isMsg ? "text" : "file"}\nMessage retracted? ${message.isRetracted ? "yes" : "no"}\nAutoMod profanity probability: ${message.autoModProb || "n/a"}\nAutoMod reviewed by UID: ${message.reviewed || "n/a"}\n\nMessage content:\n${message.text}`);
}

// Function to copy the text field of the message into the clipboard. If it is a file, copy the URL.
Expand Down
34 changes: 34 additions & 0 deletions src/css/Message.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,40 @@
background-color: rgb(41, 41, 41) !important;
}

.allow,
.deny {
/* remove the god awful default buttons */
border: none;
background: none;
color: #878a8d;
font-size: 12px;
}

.allow:hover,
.deny:hover {
cursor: pointer;
}

.allow {
color: #43b581;
}

.deny {
color: #f04747;
}

.automod {
background-color: #414141;
border-radius: 15px;
padding: 12px;
}

.waiting {
background-color: #414141;
border-radius: 15px;
padding: 12px;
}

/* I've used this same keyframe in at least three projects */
@keyframes gen {
0% {
Expand Down
1 change: 1 addition & 0 deletions src/windows/Admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function Admin() {
const tref = useRef<PopupActions | null>(null);
const tclose = () => tref.current?.close();

// TODO: Improve this menu
return (
<Popup
ref={tref}
Expand Down

0 comments on commit 4cc0b80

Please sign in to comment.