Skip to content

Commit

Permalink
add cohost rule
Browse files Browse the repository at this point in the history
  • Loading branch information
jtgi committed Mar 8, 2024
1 parent f329138 commit 97d39a0
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 48 deletions.
4 changes: 2 additions & 2 deletions app/lib/posthog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLocation } from "@remix-run/react";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { User } from "@prisma/client";
import { posthog } from "posthog-js";

Expand Down Expand Up @@ -28,7 +28,7 @@ export function usePosthog(props: { user: User | null; enabled: boolean }) {
}, [props.enabled, props.user]);

useEffect(() => {
if (enabled) {
if (props.enabled) {
posthog.capture("$pageview");
}
}, [props.enabled, location]);
Expand Down
60 changes: 50 additions & 10 deletions app/lib/validations.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
ban,
cooldown,
hideQuietly,
isCohost,
mute,
warnAndHide,
} from "./warpcast.server";
import { ModeratedChannel } from "@prisma/client";

export type RuleDefinition = {
friendlyName: string;
Expand Down Expand Up @@ -169,6 +171,14 @@ export const ruleDefinitions: Record<RuleName, RuleDefinition> = {
args: {},
},

userIsCohost: {
friendlyName: "User Is Cohost",
description: "Check if the user is a cohost",
hidden: false,
invertable: false,
args: {},
},

userFidInRange: {
friendlyName: "User FID In Range",
description: "Check if the user's FID is within a range",
Expand Down Expand Up @@ -285,6 +295,7 @@ export const ruleNames = [
"userFollowerCount",
"userIsNotActive",
"userFidInRange",
"userIsCohost",
] as const;

export const actionTypes = [
Expand All @@ -302,7 +313,13 @@ export const actionTypes = [
export type RuleName = (typeof ruleNames)[number];
export type ActionType = (typeof actionTypes)[number];

export type CheckFunction = (cast: Cast, rule: Rule) => string | undefined;
export type CheckFunctionArgs = {
channel: ModeratedChannel;
cast: Cast;
rule: Rule;
};

export type CheckFunction = (props: CheckFunctionArgs) => string | undefined;
export type ActionFunction<T = any> = (args: {
channel: string;
cast: Cast;
Expand Down Expand Up @@ -377,6 +394,7 @@ export const ruleFunctions: Record<RuleName, CheckFunction> = {
containsTooManyMentions: containsTooManyMentions,
containsLinks: containsLinks,
userProfileContainsText: userProfileContainsText,
userIsCohost: userIsCohost,
userDisplayNameContainsText: userDisplayNameContainsText,
userFollowerCount: userFollowerCount,
userIsNotActive: userIsNotActive,
Expand All @@ -396,7 +414,8 @@ export const actionFunctions: Record<ActionType, ActionFunction> = {
} as const;

// Rule: contains text, option to ignore case
export function containsText(cast: Cast, rule: Rule) {
export function containsText(props: CheckFunctionArgs) {
const { cast, rule } = props;
const { searchText, caseSensitive } = rule.args;

const text = caseSensitive ? cast.text : cast.text.toLowerCase();
Expand All @@ -409,7 +428,8 @@ export function containsText(cast: Cast, rule: Rule) {
}
}

export function textMatchesPattern(cast: Cast, rule: Rule) {
export function textMatchesPattern(args: CheckFunctionArgs) {
const { cast, rule } = args;
const { pattern } = rule.args;

const re2 = new RE2(pattern);
Expand All @@ -424,7 +444,8 @@ export function textMatchesPattern(cast: Cast, rule: Rule) {
}

// Rule: contains too many mentions (@...)
export function containsTooManyMentions(cast: Cast, rule: Rule) {
export function containsTooManyMentions(args: CheckFunctionArgs) {
const { cast, rule } = args;
const { maxMentions } = rule.args;

const mentions = cast.text.match(/@\w+/g) || [];
Expand All @@ -436,7 +457,8 @@ export function containsTooManyMentions(cast: Cast, rule: Rule) {
}
}

export function containsLinks(cast: Cast, rule: Rule) {
export function containsLinks(args: CheckFunctionArgs) {
const { cast, rule } = args;
const maxLinks = rule.args.maxLinks || 0;
const regex = /https?:\/\/\S+/gi;
const matches = cast.text.match(regex) || [];
Expand All @@ -448,7 +470,8 @@ export function containsLinks(cast: Cast, rule: Rule) {
}
}

export function userProfileContainsText(cast: Cast, rule: Rule) {
export function userProfileContainsText(args: CheckFunctionArgs) {
const { cast, rule } = args;
const { searchText, caseSensitive } = rule.args;
const containsText = !caseSensitive
? cast.author.profile.bio.text
Expand All @@ -463,7 +486,8 @@ export function userProfileContainsText(cast: Cast, rule: Rule) {
}
}

export function userDisplayNameContainsText(cast: Cast, rule: Rule) {
export function userDisplayNameContainsText(args: CheckFunctionArgs) {
const { cast, rule } = args;
const { searchText, caseSensitive } = rule.args;
const containsText = !caseSensitive
? cast.author.display_name.toLowerCase().includes(searchText.toLowerCase())
Expand All @@ -476,7 +500,8 @@ export function userDisplayNameContainsText(cast: Cast, rule: Rule) {
}
}

export function userFollowerCount(cast: Cast, rule: Rule) {
export function userFollowerCount(props: CheckFunctionArgs) {
const { cast, rule } = props;
const { min, max } = rule.args as { min?: number; max?: number };

if (min) {
Expand All @@ -492,8 +517,22 @@ export function userFollowerCount(cast: Cast, rule: Rule) {
}
}

export function userIsCohost(args: CheckFunctionArgs) {
const { channel } = args;

const isUserCohost = isCohost({
fid: args.cast.author.fid,
channel: channel.id,
});

if (!isUserCohost) {
return `User is not a cohost`;
}
}

// Rule: user active_status must be active
export function userIsNotActive(cast: Cast, rule: Rule) {
export function userIsNotActive(args: CheckFunctionArgs) {
const { cast, rule } = args;
if (!rule.invert && cast.author.active_status !== "active") {
return `User is not active`;
} else if (rule.invert && cast.author.active_status === "active") {
Expand All @@ -502,7 +541,8 @@ export function userIsNotActive(cast: Cast, rule: Rule) {
}

// Rule: user fid must be in range
export function userFidInRange(cast: Cast, rule: Rule) {
export function userFidInRange(args: CheckFunctionArgs) {
const { cast, rule } = args;
const { minFid, maxFid } = rule.args as { minFid?: number; maxFid?: number };

if (minFid) {
Expand Down
14 changes: 8 additions & 6 deletions app/routes/api.webhooks.neynar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Cast, Channel } from "@neynar/nodejs-sdk/build/neynar-api/v2";
import { Prisma } from "@prisma/client";
import { ModeratedChannel, Prisma } from "@prisma/client";
import { ActionFunctionArgs, json } from "@remix-run/node";
import { db } from "~/lib/db.server";
import { getChannel } from "~/lib/neynar.server";
Expand Down Expand Up @@ -223,7 +223,7 @@ export async function validateCast({
const rule: Rule = JSON.parse(ruleSet.rule);
const actions: Action[] = JSON.parse(ruleSet.actions);

const ruleEvaluation = evaluateRules(cast, rule);
const ruleEvaluation = evaluateRules(moderatedChannel, cast, rule);

if (ruleEvaluation.didViolateRule) {
/**
Expand Down Expand Up @@ -334,6 +334,7 @@ async function logModerationAction(
}

function evaluateRules(
moderatedChannel: ModeratedChannel,
cast: Cast,
rule: Rule
):
Expand All @@ -346,11 +347,11 @@ function evaluateRules(
didViolateRule: false;
} {
if (rule.type === "CONDITION") {
return evaluateRule(cast, rule);
return evaluateRule(moderatedChannel, cast, rule);
} else if (rule.type === "LOGICAL" && rule.conditions) {
if (rule.operation === "AND") {
const evaluations = rule.conditions.map((subRule) =>
evaluateRules(cast, subRule)
evaluateRules(moderatedChannel, cast, subRule)
);
if (evaluations.every((e) => e.didViolateRule)) {
return {
Expand All @@ -367,7 +368,7 @@ function evaluateRules(
}
} else if (rule.operation === "OR") {
const results = rule.conditions.map((subRule) =>
evaluateRules(cast, subRule)
evaluateRules(moderatedChannel, cast, subRule)
);

const violation = results.find((r) => r.didViolateRule);
Expand All @@ -383,6 +384,7 @@ function evaluateRules(
}

function evaluateRule(
channel: ModeratedChannel,
cast: Cast,
rule: Rule
):
Expand All @@ -395,7 +397,7 @@ function evaluateRule(
didViolateRule: false;
} {
const check = ruleFunctions[rule.name];
const error = check(cast, rule);
const error = check({ channel, cast, rule });

if (error) {
return {
Expand Down
Loading

0 comments on commit 97d39a0

Please sign in to comment.