feat: refactored group_id -> reason

This commit is contained in:
Wyatt Johnson
2018-09-11 15:53:08 -06:00
parent 1bd841ec01
commit 7dbd2fae91
10 changed files with 327 additions and 162 deletions
@@ -30,37 +30,184 @@ scalar Cursor
## Actions
################################################################################
enum ACTION_TYPE {
"""
REACTION corresponds to a reaction to a comment from a user.
"""
REACTION
"""
CommentAction describes an Action that is left on a Comment.
"""
interface CommentAction {
id: ID!
"""
FLAG corresponds to a flag action that indicates that the given resource needs
moderator attention.
comment is the Comment that the CommentAction is about.
"""
FLAG
comment: Comment!
"""
DONT_AGREE corresponds to when a user marks a given comment that they don't
agree with.
User when available indicates the user that left the CommentAction, otherwise
it is implied that the CommentAction was left by the system.
"""
DONT_AGREE
user: User
"""
createdAt is the time that the CommentAction was created.
"""
createdAt: Time!
}
enum ACTION_ITEM_TYPE {
COMMENTS
"""
COMMENT_FLAG_REPORTED_REASON is a reason that is reported by a User on a
Comment.
"""
enum COMMENT_FLAG_REPORTED_REASON {
"""
COMMENT_REPORTED_OFFENSIVE is used when a User reported a Comment as being
offensive.
"""
COMMENT_REPORTED_OFFENSIVE
"""
COMMENT_REPORTED_SPAM is used when a User reported a Comment as appearing like
spam.
"""
COMMENT_REPORTED_SPAM
}
enum ACTION_GROUP {
SPAM_COMMENT
TOXIC_COMMENT
BODY_COUNT
TRUST
LINKS
BANNED_WORD
SUSPECT_WORD
"""
COMMENT_FLAG_DETECTED_REASON is a reason that is detected by the system on a
Comment.
"""
enum COMMENT_FLAG_DETECTED_REASON {
"""
COMMENT_DETECTED_TOXIC is used when the Comment was detected as being toxic by
the system.
"""
COMMENT_DETECTED_TOXIC
"""
COMMENT_DETECTED_SPAM is used when the Comment was detected as having spam by
the system.
"""
COMMENT_DETECTED_SPAM
"""
COMMENT_DETECTED_BODY_COUNT is used when the Comment was detected as exceeding
the body length by the system.
"""
COMMENT_DETECTED_BODY_COUNT
"""
COMMENT_DETECTED_TRUST is used when the Comment being left was done by a User
that has a low karma/trust score.
"""
COMMENT_DETECTED_TRUST
"""
COMMENT_DETECTED_LINKS is used when the Comment was detected as containing
links.
"""
COMMENT_DETECTED_LINKS
"""
COMMENT_DETECTED_BANNED_WORD is used when the Comment was detected as
containing a banned word.
"""
COMMENT_DETECTED_BANNED_WORD
"""
COMMENT_DETECTED_SUSPECT_WORD is used when the Comment was detected as
containing a suspect word.
"""
COMMENT_DETECTED_SUSPECT_WORD
}
"""
COMMENT_FLAG_REASON is the union of the COMMENT_FLAG_REPORTED_REASON
and COMMENT_FLAG_DETECTED_REASON types.
"""
enum COMMENT_FLAG_REASON {
COMMENT_REPORTED_OFFENSIVE
COMMENT_REPORTED_SPAM
COMMENT_DETECTED_TOXIC
COMMENT_DETECTED_SPAM
COMMENT_DETECTED_BODY_COUNT
COMMENT_DETECTED_TRUST
COMMENT_DETECTED_LINKS
COMMENT_DETECTED_BANNED_WORD
COMMENT_DETECTED_SUSPECT_WORD
}
"""
CommentFlagAction represents a flag Action left on a Comment.
"""
type CommentFlagAction implements CommentAction {
id: ID!
"""
reason is the reason that the CommentFlagAction was added.
"""
reason: COMMENT_FLAG_REASON!
"""
comment is the Comment that the CommentReactionAction is about.
"""
comment: Comment!
"""
User when available indicates the user that left the CommentReactionAction,
otherwise it is implied that the CommentReactionAction was left by the system.
"""
user: User
"""
createdAt is the time that the CommentReactionAction was created.
"""
createdAt: Time!
}
"""
CommentReactionAction represents a reaction Action left on a Comment.
"""
type CommentReactionAction implements CommentAction {
id: ID!
"""
comment is the Comment that the CommentReactionAction is about.
"""
comment: Comment!
"""
User when available indicates the user that left the CommentReactionAction,
otherwise it is implied that the CommentReactionAction was left by the system.
"""
user: User
"""
createdAt is the time that the CommentReactionAction was created.
"""
createdAt: Time!
}
"""
CommentDontAgreeAction represents a don't agree Action left on a Comment.
"""
type CommentDontAgreeAction implements CommentAction {
id: ID!
"""
comment is the Comment that the CommentDontAgreeAction is about.
"""
comment: Comment!
"""
User when available indicates the user that left the CommentDontAgreeAction,
otherwise it is implied that the CommentDontAgreeAction was left by the
system.
"""
user: User
"""
createdAt is the time that the CommentDontAgreeAction was created.
"""
createdAt: Time!
}
################################################################################
+53 -52
View File
@@ -1,10 +1,8 @@
import {
GQLACTION_GROUP,
GQLACTION_ITEM_TYPE,
GQLACTION_TYPE,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { GQLCOMMENT_FLAG_REASON } from "talk-server/graph/tenant/schema/__generated__/types";
import {
Action,
ACTION_ITEM_TYPE,
ACTION_TYPE,
generateActionCounts,
validateAction,
} from "talk-server/models/actions";
@@ -12,27 +10,27 @@ import {
describe("#generateActionCounts", () => {
it("generates the action counts correctly", () => {
const actions = [
{ action_type: GQLACTION_TYPE.DONT_AGREE },
{ action_type: ACTION_TYPE.DONT_AGREE },
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BANNED_WORD,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BANNED_WORD,
},
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BODY_COUNT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BODY_COUNT,
},
];
const actionCounts = generateActionCounts(...(actions as Action[]));
expect(actionCounts).toEqual({
[GQLACTION_TYPE.DONT_AGREE.toLowerCase()]: 1,
[GQLACTION_TYPE.FLAG.toLowerCase()]: 2,
[GQLACTION_TYPE.FLAG.toLowerCase() +
"_" +
GQLACTION_GROUP.BANNED_WORD.toLowerCase()]: 1,
[GQLACTION_TYPE.FLAG.toLowerCase() +
"_" +
GQLACTION_GROUP.BODY_COUNT.toLowerCase()]: 1,
[ACTION_TYPE.DONT_AGREE.toLowerCase()]: 1,
[ACTION_TYPE.FLAG.toLowerCase()]: 2,
[ACTION_TYPE.FLAG.toLowerCase() +
"_" +
GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BANNED_WORD.toLowerCase()]: 1,
[ACTION_TYPE.FLAG.toLowerCase() +
"_" +
GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BODY_COUNT.toLowerCase()]: 1,
});
});
});
@@ -41,48 +39,47 @@ describe("#validateAction", () => {
it("allows a valid action", () => {
const actions = [
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.REACTION,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.REACTION,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.DONT_AGREE,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.SPAM_COMMENT,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.DONT_AGREE,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.TOXIC_COMMENT,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SPAM,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BODY_COUNT,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_TOXIC,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.TRUST,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BODY_COUNT,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.LINKS,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_TRUST,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BANNED_WORD,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.SUSPECT_WORD,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BANNED_WORD,
},
{
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SUSPECT_WORD,
},
];
@@ -94,14 +91,18 @@ describe("#validateAction", () => {
it("does not allow an invalid action", () => {
const actions = [
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.DONT_AGREE,
group_id: GQLACTION_GROUP.SPAM_COMMENT,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.DONT_AGREE,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SPAM,
},
{
item_type: GQLACTION_ITEM_TYPE.COMMENTS,
action_type: GQLACTION_TYPE.DONT_AGREE,
group_id: GQLACTION_GROUP.BODY_COUNT,
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.DONT_AGREE,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BODY_COUNT,
},
{
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
},
];
+70 -53
View File
@@ -1,12 +1,10 @@
import Joi from "joi";
import { pick } from "lodash";
import { Db } from "mongodb";
import uuid from "uuid";
import { Omit, Sub } from "talk-common/types";
import {
GQLACTION_GROUP,
GQLACTION_ITEM_TYPE,
GQLACTION_TYPE,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { GQLCOMMENT_FLAG_REASON } from "talk-server/graph/tenant/schema/__generated__/types";
import { FilterQuery } from "talk-server/models/query";
import { TenantResource } from "talk-server/models/tenant";
@@ -16,59 +14,81 @@ function collection(db: Db) {
export type ActionCounts = Record<string, number>;
export enum ACTION_TYPE {
/**
* REACTION corresponds to a reaction to a comment from a user.
*/
REACTION = "REACTION",
/**
* FLAG corresponds to a flag action that indicates that the given resource needs
* moderator attention.
*/
FLAG = "FLAG",
/**
* DONT_AGREE corresponds to when a user marks a given comment that they don't
* agree with.
*/
DONT_AGREE = "DONT_AGREE",
}
export enum ACTION_ITEM_TYPE {
COMMENTS = "COMMENTS",
}
export interface Action extends TenantResource {
readonly id: string;
action_type: GQLACTION_TYPE;
item_type: GQLACTION_ITEM_TYPE;
action_type: ACTION_TYPE;
item_type: ACTION_ITEM_TYPE;
item_id: string;
group_id?: GQLACTION_GROUP;
reason?: GQLCOMMENT_FLAG_REASON;
user_id?: string;
created_at: Date;
metadata?: Record<string, any>;
}
const validActionCombinations: Record<
GQLACTION_ITEM_TYPE,
Record<GQLACTION_TYPE, Record<GQLACTION_GROUP, true> | true>
> = {
[GQLACTION_ITEM_TYPE.COMMENTS]: {
[GQLACTION_TYPE.REACTION]: true,
[GQLACTION_TYPE.DONT_AGREE]: true,
[GQLACTION_TYPE.FLAG]: {
[GQLACTION_GROUP.SPAM_COMMENT]: true,
[GQLACTION_GROUP.TOXIC_COMMENT]: true,
[GQLACTION_GROUP.BODY_COUNT]: true,
[GQLACTION_GROUP.TRUST]: true,
[GQLACTION_GROUP.LINKS]: true,
[GQLACTION_GROUP.BANNED_WORD]: true,
[GQLACTION_GROUP.SUSPECT_WORD]: true,
},
const ActionSchema = [
// Flags
{
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.FLAG,
// Only reasons for the flag action will be allowed here, and it must be
// specified.
reason: Object.keys(GQLCOMMENT_FLAG_REASON),
},
};
// Don't Agree
{
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.DONT_AGREE,
},
// Reaction
{
item_type: ACTION_ITEM_TYPE.COMMENTS,
action_type: ACTION_TYPE.REACTION,
},
];
/**
* validateAction is used to validate that a specific action conforms to the
* expected schema, `ActionSchema`.
*/
export function validateAction(
action: Pick<Action, "item_type" | "action_type" | "group_id">
action: Pick<Action, "item_type" | "action_type" | "reason">
) {
if (!(action.item_type in validActionCombinations)) {
throw new Error("invalid item_type");
}
const itemType = validActionCombinations[action.item_type];
if (!(action.action_type in itemType)) {
throw new Error("invalid action_type");
}
const actionType = itemType[action.action_type];
if (action.group_id) {
if (actionType === true) {
throw new Error("invalid action_type");
const { error } = Joi.validate(
// In typescript, this isn't an issue, but when this is transpiled to
// javascript, it will contain additional elements.
pick(action, ["item_type", "action_type", "reason"]),
ActionSchema,
{
presence: "required",
abortEarly: false,
}
if (!(action.group_id in actionType)) {
throw new Error("invalid action_type");
}
} else if (actionType !== true) {
throw new Error("invalid action_type");
);
if (error) {
// TODO: wrap error?
throw error;
}
}
@@ -93,9 +113,6 @@ export async function createAction(
tenantID: string,
input: CreateActionInput
): Promise<CreateActionResultObject> {
// Validate that the action is valid, if it isn't, this will throw an error.
validateAction(input);
// Create a new ID for the action.
const id = uuid.v4();
@@ -119,7 +136,7 @@ export async function createAction(
action_type: input.action_type,
item_type: input.item_type,
item_id: input.item_id,
group_id: input.group_id,
reason: input.reason,
user_id: input.user_id,
};
@@ -191,11 +208,11 @@ export function generateActionCounts(...actions: Action[]): ActionCounts {
// Add the action type to the action counts.
incr(actionType);
// Check if the group id is set.
const groupID = action.group_id && action.group_id.toLowerCase();
if (groupID) {
// Check if the reason is set.
const reason = action.reason && action.reason.toLowerCase();
if (reason) {
// Add the action type to the action counts.
incr(actionType, groupID);
incr(actionType, reason);
}
}
@@ -1,8 +1,8 @@
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { ACTION_TYPE } from "talk-server/models/actions";
import {
compose,
ModerationPhaseContext,
@@ -51,12 +51,12 @@ describe("compose", () => {
const flags = [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.TOXIC_COMMENT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_TOXIC,
},
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.SPAM_COMMENT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SPAM,
},
];
@@ -71,8 +71,8 @@ describe("compose", () => {
() => ({
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.LINKS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
},
],
}),
@@ -85,8 +85,8 @@ describe("compose", () => {
}
expect(final.actions).not.toContainEqual({
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.LINKS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
});
});
@@ -1,8 +1,8 @@
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { ACTION_TYPE } from "talk-server/models/actions";
import { ModerationSettings } from "talk-server/models/settings";
import {
IntermediateModerationPhase,
@@ -35,8 +35,8 @@ export const commentLength: IntermediateModerationPhase = ({
status: GQLCOMMENT_STATUS.REJECTED,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BODY_COUNT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BODY_COUNT,
metadata: {
count: length,
},
@@ -1,8 +1,8 @@
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { ACTION_TYPE } from "talk-server/models/actions";
import {
IntermediateModerationPhase,
IntermediatePhaseResult,
@@ -33,8 +33,8 @@ export const karma: IntermediateModerationPhase = ({
status: GQLCOMMENT_STATUS.SYSTEM_WITHHELD,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.TRUST,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_TOXIC,
metadata: {
trust: getCommentTrustScore(author),
},
@@ -2,10 +2,10 @@ import linkify from "linkify-it";
import tlds from "tlds";
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { ACTION_TYPE } from "talk-server/models/actions";
import { ModerationSettings } from "talk-server/models/settings";
import {
IntermediateModerationPhase,
@@ -39,8 +39,8 @@ export const links: IntermediateModerationPhase = ({
status: GQLCOMMENT_STATUS.SYSTEM_WITHHELD,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.LINKS,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
metadata: {
links: comment.body,
},
@@ -1,11 +1,11 @@
import { Client } from "akismet-api";
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import logger from "talk-server/logger";
import { ACTION_TYPE } from "talk-server/models/actions";
import {
IntermediateModerationPhase,
IntermediatePhaseResult,
@@ -110,8 +110,8 @@ export const spam: IntermediateModerationPhase = async ({
status: GQLCOMMENT_STATUS.SYSTEM_WITHHELD,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.SPAM_COMMENT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SPAM,
},
],
metadata: {
@@ -4,12 +4,12 @@ import fetch from "node-fetch";
import { Omit } from "talk-common/types";
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
GQLPerspectiveExternalIntegration,
} from "talk-server/graph/tenant/schema/__generated__/types";
import logger from "talk-server/logger";
import { ACTION_TYPE } from "talk-server/models/actions";
import {
IntermediateModerationPhase,
IntermediatePhaseResult,
@@ -103,8 +103,8 @@ export const toxic: IntermediateModerationPhase = async ({
status: GQLCOMMENT_STATUS.SYSTEM_WITHHELD,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.TOXIC_COMMENT,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_TOXIC,
},
],
metadata: {
@@ -1,8 +1,8 @@
import {
GQLACTION_GROUP,
GQLACTION_TYPE,
GQLCOMMENT_FLAG_REASON,
GQLCOMMENT_STATUS,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { ACTION_TYPE } from "talk-server/models/actions";
import {
IntermediateModerationPhase,
IntermediatePhaseResult,
@@ -29,8 +29,8 @@ export const wordlist: IntermediateModerationPhase = ({
status: GQLCOMMENT_STATUS.REJECTED,
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.BANNED_WORD,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_BANNED_WORD,
},
],
};
@@ -46,8 +46,8 @@ export const wordlist: IntermediateModerationPhase = ({
return {
actions: [
{
action_type: GQLACTION_TYPE.FLAG,
group_id: GQLACTION_GROUP.SUSPECT_WORD,
action_type: ACTION_TYPE.FLAG,
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_SUSPECT_WORD,
},
],
};