Compare commits

..

1 Commits

Author SHA1 Message Date
FoxxMD
eee2a8e856 feat: Support multiple expressions for regex property 2022-11-22 16:03:50 -05:00
20 changed files with 393 additions and 3307 deletions

View File

@@ -61,14 +61,13 @@ All Actions with `content` have access to this data:
Additionally, `author` has these properties accessible:
| Name | Description | Example |
|----------------|-----------------------------------------------------------------------------------|------------|
| `age` | (Approximate) Age of account | 3 months |
| `linkKarma` | Amount of link karma | 10 |
| `commentKarma` | Amount of comment karma | 3 |
| `totalKarma` | Combined link+comment karma | 13 |
| `verified` | Does account have a verified email? | true |
| `flairText` | The text of the Flair assigned to the Author in this subreddit, if one is present | Test Flair |
| Name | Description | Example |
|----------------|-------------------------------------|----------|
| `age` | (Approximate) Age of account | 3 months |
| `linkKarma` | Amount of link karma | 10 |
| `commentKarma` | Amount of comment karma | 3 |
| `totalKarma` | Combined link+comment karma | 13 |
| `verified` | Does account have a verified email? | true |
NOTE: Accessing these properties may require an additional API call so use sparingly on high-volume comments
@@ -85,14 +84,13 @@ Produces:
If the **Activity** is a Submission these additional properties are accessible:
| Name | Description | Example |
|-------------------|-----------------------------------------------------------------|-------------------------|
| `upvoteRatio` | The upvote ratio | 100% |
| `nsfw` | If the submission is marked as NSFW | true |
| `spoiler` | If the submission is marked as a spoiler | true |
| `url` | If the submission was a link then this is the URL for that link | http://example.com |
| `title` | The title of the submission | Test post please ignore |
| `link_flair_text` | The flair text assigned to this submission | Test Flair |
| Name | Description | Example |
|---------------|-----------------------------------------------------------------|-------------------------|
| `upvoteRatio` | The upvote ratio | 100% |
| `nsfw` | If the submission is marked as NSFW | true |
| `spoiler` | If the submission is marked as a spoiler | true |
| `url` | If the submission was a link then this is the URL for that link | http://example.com |
| `title` | The title of the submission | Test post please ignore |
### Comments

View File

@@ -67,15 +67,13 @@ export class BanAction extends Action {
// @ts-ignore
const fetchedSub = await item.subreddit.fetch();
const fetchedName = await item.author.name;
const banData = {
const bannedUser = await fetchedSub.banUser({
name: fetchedName,
banMessage: renderedContent === undefined ? undefined : renderedContent,
banReason: renderedReason,
banNote: renderedNote,
duration: this.duration
};
const bannedUser = await fetchedSub.banUser(banData);
await this.resources.addUserToSubredditBannedUserCache(banData)
});
touchedEntities.push(bannedUser);
}
return {

View File

@@ -43,7 +43,11 @@ export class FlairAction extends Action {
if (item instanceof Submission) {
if(!this.dryRun) {
if (this.flair_template_id) {
await item.selectFlair({flair_template_id: this.flair_template_id}).then(() => {});
// typings are wrong for this function, flair_template_id should be accepted
// assignFlair uses /api/flair (mod endpoint)
// selectFlair uses /api/selectflair (self endpoint for user to choose their own flair for submission)
// @ts-ignore
await item.assignFlair({flair_template_id: this.flair_template_id}).then(() => {});
item.link_flair_template_id = this.flair_template_id;
} else {
await item.assignFlair({text: renderedText, cssClass: renderedCss}).then(() => {});

View File

@@ -408,9 +408,3 @@ export interface RuleResultsTemplateData {
export interface GenericContentTemplateData extends BaseTemplateData, Partial<RuleResultsTemplateData>, Partial<ActionResultsTemplateData> {
item?: (SubmissionTemplateData | CommentTemplateData)
}
export type SubredditPlaceholderType = '{{subreddit}}';
export const subredditPlaceholder: SubredditPlaceholderType = '{{subreddit}}';
export const asSubredditPlaceholder = (val: any): val is SubredditPlaceholderType => {
return typeof val === 'string' && val.toLowerCase() === '{{subreddit}}';
}

View File

@@ -4,7 +4,7 @@ import {
DurationComparor,
ModeratorNameCriteria,
ModeratorNames, ModActionType,
ModUserNoteLabel, RelativeDateTimeMatch, SubredditPlaceholderType
ModUserNoteLabel, RelativeDateTimeMatch
} from "../Atomic";
import {ActivityType, MaybeActivityType} from "../Reddit";
import {GenericComparison, parseGenericValueComparison} from "../Comparisons";
@@ -57,7 +57,7 @@ export interface SubredditCriteria {
}
export interface StrongSubredditCriteria extends SubredditCriteria {
name?: RegExp | SubredditPlaceholderType
name?: RegExp
}
export const defaultStrongSubredditCriteriaOptions = {
@@ -289,57 +289,7 @@ export const toFullModLogCriteria = (val: ModLogCriteria): FullModLogCriteria =>
}, {});
}
export const authorCriteriaProperties = ['name', 'flairCssClass', 'flairText', 'flairTemplate', 'isMod', 'userNotes', 'modActions', 'age', 'linkKarma', 'commentKarma', 'totalKarma', 'verified', 'shadowBanned', 'description', 'isContributor', 'banned'];
export interface BanCriteria {
/**
* Test when the Author was banned against this comparison
*
* The syntax is `(< OR > OR <= OR >=) <number> <unit>`
*
* * EX `> 100 days` => Passes if Author was banned more than 100 days ago
* * EX `<= 2 months` => Passes if Author was banned less than or equal to 2 months ago
*
* Unit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)
*
* [See] https://regexr.com/609n8 for example
*
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\s*$
* */
bannedAt?: DurationComparor
/**
* A string or list of strings to match ban note against.
*
* If a list then ANY matched string makes this pass.
*
* String may be a regular expression enclosed in forward slashes. If it is not a regular expression then it is matched case-insensitive.
* */
note?: string | string[]
/**
* Test how many days are left for Author's ban against this comparison
*
* If the ban is permanent then the number of days left is equivalent to **INFINITY**
*
* The syntax is `(< OR > OR <= OR >=) <number> <unit>`
*
* * EX `> 100 days` => Passes if the Author's ban has more than 100 days left
* * EX `<= 2 months` => Passes if Author's ban has equal to or less than 2 months left
*
* Unit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)
*
* [See] https://regexr.com/609n8 for example
*
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\s*$
* */
daysLeft?: DurationComparor
/**
* Is the ban permanent?
* */
permanent?: boolean
}
export const authorCriteriaProperties = ['name', 'flairCssClass', 'flairText', 'flairTemplate', 'isMod', 'userNotes', 'modActions', 'age', 'linkKarma', 'commentKarma', 'totalKarma', 'verified', 'shadowBanned', 'description', 'isContributor'];
/**
* Criteria with which to test against the author of an Activity. The outcome of the test is based on:
@@ -478,19 +428,6 @@ export interface AuthorCriteria {
* Is the author an approved user (contributor)?
* */
isContributor?: boolean
/**
* Is the Author banned or not?
*
* If user is not banned but BanCriteria(s) is present the test will fail
*
* * Use a boolean true/false for a simple yes or no
* * Or use a BanCriteria to test for specifics of an existing ban
* * Or use a list of BanCriteria -- if ANY BanCriteria passes the test passes
*
* NOTE: USE WITH CARE! This criteria usually incurs 1 API call
* */
banned?: boolean | BanCriteria | BanCriteria[]
}
/**
@@ -518,7 +455,6 @@ export const orderedAuthorCriteriaProps: (keyof AuthorCriteria)[] = [
'isMod', // requires fetching mods for subreddit
'isContributor', // requires fetching contributors for subreddit
'modActions', // requires fetching mod notes/actions for author (shortest cache TTL)
'banned', // requires fetching /about/banned for every user not cached
];
export interface ActivityState {

View File

@@ -1,9 +1,6 @@
import {Comment, RedditUser, Submission, Subreddit} from "snoowrap/dist/objects";
import { BannedUser } from "snoowrap/dist/objects/Subreddit";
import { ValueOf } from "ts-essentials";
import {CMError} from "../../Utils/Errors";
import {Dayjs} from "dayjs";
import {Duration} from "dayjs/plugin/duration.js";
export type ActivityType = 'submission' | 'comment';
export type MaybeActivityType = ActivityType | false;
@@ -169,16 +166,3 @@ export interface RedditRemovalMessageOptions {
title?: string
lock?: boolean
}
export interface CMBannedUser extends Omit<SnoowrapBannedUser, 'days_left' | 'date'> {
user: RedditUser
date: Dayjs
days_left: undefined | Duration
}
export interface SnoowrapBannedUser extends Omit<BannedUser, 'id'> {
days_left: number | null
rel_id?: string
id?: string
}

View File

@@ -748,6 +748,10 @@ export interface RegExResult {
named: NamedGroup
}
export interface RegExResultWithTest extends RegExResult {
test: RegExp
}
export type StrongCache = {
authorTTL: number | boolean,
userNotesTTL: number | boolean,

View File

@@ -7,6 +7,7 @@ import {
PASS, triggeredIndicator, windowConfigToWindowCriteria
} from "../util";
import {
RegExResultWithTest,
RuleResult,
} from "../Common/interfaces";
import dayjs from 'dayjs';
@@ -14,10 +15,11 @@ import {SimpleError} from "../Utils/Errors";
import {JoinOperands} from "../Common/Infrastructure/Atomic";
import {ActivityWindowConfig} from "../Common/Infrastructure/ActivityWindow";
import {
comparisonTextOp,
comparisonTextOp, GenericComparison,
parseGenericValueComparison,
parseGenericValueOrPercentComparison
} from "../Common/Infrastructure/Comparisons";
import {SnoowrapActivity} from "../Common/Infrastructure/Reddit";
export interface RegexCriteria {
/**
@@ -27,13 +29,23 @@ export interface RegexCriteria {
* */
name?: string
/**
* A valid Regular Expression to test content against
* A valid Regular Expression, or list of expressions, to test content against
*
* If no flags are specified then the **global** flag is used by default
*
* @examples ["/reddit|FoxxMD/ig"]
* */
regex: string,
regex: string | string[],
/**
* Determines if ALL regexes listed are run or if regexes are only run until one is matched.
*
* * `true` => all regexes are always run
* * `false` => regexes are run until one matches
*
* @default false
* */
exhaustive?: boolean
/**
* Which content from an Activity to test the regex against
@@ -157,6 +169,7 @@ export class RegexRule extends Rule {
const {
name = (index + 1),
regex,
exhaustive = false,
testOn: testOnVals = ['title', 'body'],
lookAt = 'all',
matchThreshold = '> 0',
@@ -174,13 +187,7 @@ export class RegexRule extends Rule {
return acc.concat(curr);
}, []);
// check regex
const regexContent = await this.resources.getContent(regex);
const reg = parseStringToRegex(regexContent, 'g');
if(reg === undefined) {
throw new SimpleError(`Value given for regex on Criteria ${name} was not valid: ${regex}`);
}
// ok cool its a valid regex
const regexTests: RegExp[] = await this.convertToRegexArray(name, regex);
const matchComparison = parseGenericValueComparison(matchThreshold);
const activityMatchComparison = activityMatchThreshold === null ? undefined : parseGenericValueOrPercentComparison(activityMatchThreshold);
@@ -198,12 +205,13 @@ export class RegexRule extends Rule {
// first lets see if the activity we are checking satisfies thresholds
// since we may be able to avoid api calls to get history
let actMatches = this.getMatchesFromActivity(item, testOn, reg);
matches = matches.concat(actMatches).slice(0, 100);
matchCount += actMatches.length;
let actMatches = getMatchesFromActivity(item, testOn, regexTests, exhaustive);
const actMatchSummary = regexResultsSummary(actMatches);
matches = matches.concat(actMatchSummary.matches).slice(0, 100);
matchCount += actMatchSummary.matches.length;
activitiesTested++;
const singleMatched = comparisonTextOp(actMatches.length, matchComparison.operator, matchComparison.value);
const singleMatched = comparisonTextOp(actMatchSummary.matches.length, matchComparison.operator, matchComparison.value);
if (singleMatched) {
activitiesMatchedCount++;
}
@@ -233,7 +241,7 @@ export class RegexRule extends Rule {
}
history = await this.resources.getAuthorActivities(item.author, strongWindow);
// remove current activity it exists in history so we don't count it twice
// remove current activity if it exists in history so we don't count it twice
history = history.filter(x => x.id !== item.id);
const historyLength = history.length;
@@ -252,10 +260,12 @@ export class RegexRule extends Rule {
for (const h of history) {
activitiesTested++;
const aMatches = this.getMatchesFromActivity(h, testOn, reg);
matches = matches.concat(aMatches).slice(0, 100);
matchCount += aMatches.length;
const matched = comparisonTextOp(aMatches.length, matchComparison.operator, matchComparison.value);
const aMatches = getMatchesFromActivity(h, testOn, regexTests, exhaustive);
actMatches = actMatches.concat(aMatches);
const actHistoryMatchSummary = regexResultsSummary(aMatches);
matches = matches.concat(actHistoryMatchSummary.matches).slice(0, 100);
matchCount += actHistoryMatchSummary.matches.length;
const matched = comparisonTextOp(actHistoryMatchSummary.matches.length, matchComparison.operator, matchComparison.value);
if (matched) {
activitiesMatchedCount++;
}
@@ -282,10 +292,19 @@ export class RegexRule extends Rule {
humanWindow = '1 Item';
}
// to provide at least one useful regex for this criteria
// use the first regex found by default
let relevantRegex: string = regexTests[0].toString();
// but if more than one regex was listed AND we did have matches
// then use the first regex that actually got a match
if(regexTests.length > 0 && actMatches.length > 0) {
relevantRegex = actMatches[0].test.toString();
}
const critResults = {
criteria: {
name,
regex: regex !== regexContent ? `${regex} from ${regexContent}` : regex,
regex: relevantRegex,
testOn,
matchThreshold,
activityMatchThreshold,
@@ -352,44 +371,117 @@ export class RegexRule extends Rule {
return Promise.resolve([criteriaMet, this.getResult(criteriaMet, {result, data: {results: criteriaResults, matchSample }})]);
}
protected getMatchesFromActivity(a: (Submission | Comment), testOn: string[], reg: RegExp): string[] {
let m: string[] = [];
// determine what content we are testing
let contents: string[] = [];
if (asSubmission(a)) {
for (const l of testOn) {
switch (l) {
case 'title':
contents.push(a.title);
break;
case 'body':
if (a.is_self) {
contents.push(a.selftext);
}
break;
case 'url':
if (isExternalUrlSubmission(a)) {
contents.push(a.url);
}
break;
}
protected async convertToRegexArray(name: string | number, value: string | string[]): Promise<RegExp[]> {
const regexTests: RegExp[] = [];
const regexStringVals = typeof value === 'string' ? [value] : value;
for(const r of regexStringVals) {
// check regex
const regexContent = await this.resources.getContent(r);
const reg = parseStringToRegex(regexContent, 'ig');
if (reg === undefined) {
throw new SimpleError(`Value given for regex on Criteria ${name} was not valid: ${value}`);
}
} else {
contents.push(a.body)
// ok cool its a valid regex
regexTests.push(reg);
}
for (const c of contents) {
const results = parseRegex(reg, c);
if(results !== undefined) {
for(const r of results) {
m.push(r.match);
}
}
}
return m;
return regexTests;
}
}
export const getMatchResultsFromContent = (contents: string[], reg: RegExp): RegExResultWithTest[] => {
let m: RegExResultWithTest[] = [];
for (const c of contents) {
const results = parseRegex(reg, c);
if(results !== undefined) {
for(const r of results) {
m.push({...r, test: reg});
}
}
}
return m;
}
export const regexResultsSummary = (results: RegExResultWithTest[]) => {
const matchResults: ActivityMatchResults = {
matches: [],
matchesByTest: {},
groups: {}
}
for (const r of results) {
if (matchResults.matchesByTest[r.test.toString()] === undefined) {
matchResults.matchesByTest[r.test.toString()] = [];
}
matchResults.matchesByTest[r.test.toString()].push(r.match);
matchResults.matches.push(r.match);
if (r.named !== undefined) {
Object.entries(r.named).forEach(([key, val]) => {
if (matchResults.groups[key] === undefined) {
matchResults.groups[key] = [];
}
matchResults.groups[key].push(val);
});
}
}
return matchResults;
}
export const getMatchesFromActivity = (a: (Submission | Comment), testOn: string[], regexes: RegExp[], exhaustive: boolean): RegExResultWithTest[] => {
// determine what content we are testing
let contents: string[] = getMatchableContent(a, testOn);
let results: RegExResultWithTest[] = [];
for (const reg of regexes) {
const res = getMatchResultsFromContent(contents, reg);
if(res.length > 0) {
results = results.concat(res);
// only continue testing if the user wants to exhaustively check all regexes (to get more matches?)
if(!exhaustive) {
return results;
}
}
}
return results;
}
const getMatchableContent = (a: SnoowrapActivity, testOn: string[]) => {
let contents: string[] = [];
if (asSubmission(a)) {
for (const l of testOn) {
switch (l) {
case 'title':
contents.push(a.title);
break;
case 'body':
if (a.is_self) {
contents.push(a.selftext);
}
break;
case 'url':
if (isExternalUrlSubmission(a)) {
contents.push(a.url);
}
break;
}
}
} else {
contents.push(a.body)
}
return contents;
}
interface RegexMatchComparisonOptions {
matchComparison: GenericComparison
activityMatchComparison?: GenericComparison
totalMatchComparison?: GenericComparison
}
interface ActivityMatchResults {
matches: string[]
matchesByTest: Record<string, string[]>
groups: Record<string, string[]>
}
interface RegexConfig {
/**
* A list of Regular Expressions and conditions under which tested Activity(ies) are matched

View File

@@ -59,12 +59,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -83,9 +77,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -109,15 +100,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -139,9 +121,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -201,23 +180,6 @@
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"banned": {
"anyOf": [
{
"$ref": "#/definitions/BanCriteria"
},
{
"items": {
"$ref": "#/definitions/BanCriteria"
},
"type": "array"
},
{
"type": "boolean"
}
],
"description": "Is the Author banned or not?\n\nIf user is not banned but BanCriteria(s) is present the test will fail\n\n* Use a boolean true/false for a simple yes or no\n* Or use a BanCriteria to test for specifics of an existing ban\n* Or use a list of BanCriteria -- if ANY BanCriteria passes the test passes\n\nNOTE: USE WITH CARE! This criteria usually incurs 1 API call"
},
"commentKarma": {
"description": "A string containing a comparison operator and a value to compare karma against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 comment karma\n* EX `<= 75%` => comment karma is less than or equal to 75% of **all karma**",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
@@ -383,12 +345,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -407,9 +363,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -456,15 +409,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -486,9 +430,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -537,50 +478,11 @@
],
"type": "object"
},
"BanCriteria": {
"properties": {
"bannedAt": {
"description": "Test when the Author was banned against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author was banned more than 100 days ago\n* EX `<= 2 months` => Passes if Author was banned less than or equal to 2 months ago\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"daysLeft": {
"description": "Test how many days are left for Author's ban against this comparison\n\nIf the ban is permanent then the number of days left is equivalent to **INFINITY**\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if the Author's ban has more than 100 days left\n* EX `<= 2 months` => Passes if Author's ban has equal to or less than 2 months left\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"note": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "string"
}
],
"description": "A string or list of strings to match ban note against.\n\nIf a list then ANY matched string makes this pass.\n\nString may be a regular expression enclosed in forward slashes. If it is not a regular expression then it is matched case-insensitive."
},
"permanent": {
"description": "Is the ban permanent?",
"type": "boolean"
}
},
"type": "object"
},
"CancelDispatchActionJson": {
"description": "Remove the Activity",
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -599,9 +501,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -641,15 +540,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -671,9 +561,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -726,18 +613,8 @@
"CommentActionJson": {
"description": "Reply to the Activity. For a submission the reply will be a top-level comment.",
"properties": {
"asModTeam": {
"description": "Comment \"as subreddit\" using the \"/u/subreddit-ModTeam\" account\n\nRESTRICTIONS:\n\n* Target activity must ALREADY BE REMOVED\n* Will always distinguish and sticky the created comment",
"type": "boolean"
},
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -756,9 +633,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -804,15 +678,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -834,9 +699,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -1107,12 +969,6 @@
},
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1131,9 +987,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1157,15 +1010,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1187,9 +1031,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -1228,12 +1069,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1252,9 +1087,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1340,15 +1172,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1370,9 +1193,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -1605,12 +1425,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1629,9 +1443,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1663,15 +1474,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1693,9 +1495,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -1765,12 +1564,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1789,9 +1582,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1815,15 +1605,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1845,9 +1626,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -1882,12 +1660,6 @@
},
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1906,9 +1678,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1950,15 +1719,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1980,9 +1740,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2146,12 +1903,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2170,9 +1921,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2220,15 +1968,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2250,9 +1989,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2486,12 +2222,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2510,9 +2240,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2536,15 +2263,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2566,9 +2284,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2611,12 +2326,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2635,9 +2344,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2670,15 +2376,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2700,9 +2397,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2733,12 +2427,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2757,9 +2445,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2818,15 +2503,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2848,9 +2524,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3212,12 +2885,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3236,9 +2903,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3270,15 +2934,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3300,9 +2955,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3345,12 +2997,6 @@
},
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3369,9 +3015,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3419,15 +3062,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3449,9 +3083,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -24,23 +24,6 @@
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"banned": {
"anyOf": [
{
"$ref": "#/definitions/BanCriteria"
},
{
"items": {
"$ref": "#/definitions/BanCriteria"
},
"type": "array"
},
{
"type": "boolean"
}
],
"description": "Is the Author banned or not?\n\nIf user is not banned but BanCriteria(s) is present the test will fail\n\n* Use a boolean true/false for a simple yes or no\n* Or use a BanCriteria to test for specifics of an existing ban\n* Or use a list of BanCriteria -- if ANY BanCriteria passes the test passes\n\nNOTE: USE WITH CARE! This criteria usually incurs 1 API call"
},
"commentKarma": {
"description": "A string containing a comparison operator and a value to compare karma against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 comment karma\n* EX `<= 75%` => comment karma is less than or equal to 75% of **all karma**",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
@@ -201,39 +184,6 @@
},
"type": "object"
},
"BanCriteria": {
"properties": {
"bannedAt": {
"description": "Test when the Author was banned against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author was banned more than 100 days ago\n* EX `<= 2 months` => Passes if Author was banned less than or equal to 2 months ago\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"daysLeft": {
"description": "Test how many days are left for Author's ban against this comparison\n\nIf the ban is permanent then the number of days left is equivalent to **INFINITY**\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if the Author's ban has more than 100 days left\n* EX `<= 2 months` => Passes if Author's ban has equal to or less than 2 months left\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"note": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "string"
}
],
"description": "A string or list of strings to match ban note against.\n\nIf a list then ANY matched string makes this pass.\n\nString may be a regular expression enclosed in forward slashes. If it is not a regular expression then it is matched case-insensitive."
},
"permanent": {
"description": "Is the ban permanent?",
"type": "boolean"
}
},
"type": "object"
},
"BotConnection": {
"description": "Configuration required to connect to a CM Server",
"properties": {

View File

@@ -432,10 +432,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -461,12 +461,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -485,9 +479,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -510,15 +501,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -540,9 +522,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -591,23 +570,6 @@
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"banned": {
"anyOf": [
{
"$ref": "#/definitions/BanCriteria"
},
{
"items": {
"$ref": "#/definitions/BanCriteria"
},
"type": "array"
},
{
"type": "boolean"
}
],
"description": "Is the Author banned or not?\n\nIf user is not banned but BanCriteria(s) is present the test will fail\n\n* Use a boolean true/false for a simple yes or no\n* Or use a BanCriteria to test for specifics of an existing ban\n* Or use a list of BanCriteria -- if ANY BanCriteria passes the test passes\n\nNOTE: USE WITH CARE! This criteria usually incurs 1 API call"
},
"commentKarma": {
"description": "A string containing a comparison operator and a value to compare karma against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 comment karma\n* EX `<= 75%` => comment karma is less than or equal to 75% of **all karma**",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
@@ -772,12 +734,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -796,9 +752,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -833,15 +786,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -863,9 +807,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -891,39 +832,6 @@
],
"type": "object"
},
"BanCriteria": {
"properties": {
"bannedAt": {
"description": "Test when the Author was banned against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author was banned more than 100 days ago\n* EX `<= 2 months` => Passes if Author was banned less than or equal to 2 months ago\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"daysLeft": {
"description": "Test how many days are left for Author's ban against this comparison\n\nIf the ban is permanent then the number of days left is equivalent to **INFINITY**\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if the Author's ban has more than 100 days left\n* EX `<= 2 months` => Passes if Author's ban has equal to or less than 2 months left\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"note": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "string"
}
],
"description": "A string or list of strings to match ban note against.\n\nIf a list then ANY matched string makes this pass.\n\nString may be a regular expression enclosed in forward slashes. If it is not a regular expression then it is matched case-insensitive."
},
"permanent": {
"description": "Is the ban permanent?",
"type": "boolean"
}
},
"type": "object"
},
"CommentState": {
"description": "Different attributes a `Comment` can be in. Only include a property if you want to check it.",
"examples": [
@@ -1618,10 +1526,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1672,10 +1580,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1722,10 +1630,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1759,10 +1667,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1812,12 +1720,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1836,9 +1738,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1909,15 +1808,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1939,9 +1829,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2112,12 +1999,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2136,9 +2017,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2153,15 +2031,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2183,9 +2052,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2691,12 +2557,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2715,9 +2575,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2728,15 +2585,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2758,9 +2606,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2812,10 +2657,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -2957,10 +2802,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -2985,12 +2830,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3009,9 +2848,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3041,15 +2877,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3071,9 +2898,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3108,12 +2932,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3132,9 +2950,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3198,15 +3013,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3228,9 +3034,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3294,10 +3097,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3429,10 +3232,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3454,12 +3257,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3478,9 +3275,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3510,15 +3304,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3540,9 +3325,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3653,10 +3435,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3681,12 +3463,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3705,9 +3481,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3736,15 +3509,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3766,9 +3530,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"

View File

@@ -397,10 +397,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -426,12 +426,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -450,9 +444,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -475,15 +466,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -505,9 +487,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -556,23 +535,6 @@
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"banned": {
"anyOf": [
{
"$ref": "#/definitions/BanCriteria"
},
{
"items": {
"$ref": "#/definitions/BanCriteria"
},
"type": "array"
},
{
"type": "boolean"
}
],
"description": "Is the Author banned or not?\n\nIf user is not banned but BanCriteria(s) is present the test will fail\n\n* Use a boolean true/false for a simple yes or no\n* Or use a BanCriteria to test for specifics of an existing ban\n* Or use a list of BanCriteria -- if ANY BanCriteria passes the test passes\n\nNOTE: USE WITH CARE! This criteria usually incurs 1 API call"
},
"commentKarma": {
"description": "A string containing a comparison operator and a value to compare karma against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 comment karma\n* EX `<= 75%` => comment karma is less than or equal to 75% of **all karma**",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
@@ -737,12 +699,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -761,9 +717,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -798,15 +751,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -828,9 +772,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -856,39 +797,6 @@
],
"type": "object"
},
"BanCriteria": {
"properties": {
"bannedAt": {
"description": "Test when the Author was banned against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author was banned more than 100 days ago\n* EX `<= 2 months` => Passes if Author was banned less than or equal to 2 months ago\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"daysLeft": {
"description": "Test how many days are left for Author's ban against this comparison\n\nIf the ban is permanent then the number of days left is equivalent to **INFINITY**\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if the Author's ban has more than 100 days left\n* EX `<= 2 months` => Passes if Author's ban has equal to or less than 2 months left\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"note": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "string"
}
],
"description": "A string or list of strings to match ban note against.\n\nIf a list then ANY matched string makes this pass.\n\nString may be a regular expression enclosed in forward slashes. If it is not a regular expression then it is matched case-insensitive."
},
"permanent": {
"description": "Is the ban permanent?",
"type": "boolean"
}
},
"type": "object"
},
"CommentState": {
"description": "Different attributes a `Comment` can be in. Only include a property if you want to check it.",
"examples": [
@@ -1583,10 +1491,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1637,10 +1545,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1687,10 +1595,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1724,10 +1632,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -1777,12 +1685,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -1801,9 +1703,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -1874,15 +1773,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -1904,9 +1794,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2077,12 +1964,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2101,9 +1982,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2118,15 +1996,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2148,9 +2017,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2656,12 +2522,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2680,9 +2540,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -2693,15 +2550,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -2723,9 +2571,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -2777,10 +2622,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -2922,10 +2767,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -2950,12 +2795,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -2974,9 +2813,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3006,15 +2842,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3036,9 +2863,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3073,12 +2897,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3097,9 +2915,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3163,15 +2978,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3193,9 +2999,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3259,10 +3062,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3394,10 +3197,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3419,12 +3222,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3443,9 +3240,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3475,15 +3269,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3505,9 +3290,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"
@@ -3618,10 +3400,10 @@
"window": {
"anyOf": [
{
"$ref": "#/definitions/FullActivityWindowConfig"
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/DurationObject"
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
@@ -3646,12 +3428,6 @@
"properties": {
"authorIs": {
"anyOf": [
{
"$ref": "#/definitions/AuthorCriteria"
},
{
"$ref": "#/definitions/NamedCriteria<AuthorCriteria>"
},
{
"items": {
"anyOf": [
@@ -3670,9 +3446,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<AuthorCriteria>"
},
{
"type": "string"
}
],
"description": "If present then these Author criteria are checked before running the Check. If criteria fails then the Check will fail."
@@ -3701,15 +3474,6 @@
},
"itemIs": {
"anyOf": [
{
"$ref": "#/definitions/SubmissionState"
},
{
"$ref": "#/definitions/CommentState"
},
{
"$ref": "#/definitions/NamedCriteria<TypedActivityState>"
},
{
"items": {
"anyOf": [
@@ -3731,9 +3495,6 @@
},
{
"$ref": "#/definitions/FilterOptionsJson<TypedActivityState>"
},
{
"type": "string"
}
],
"description": "A list of criteria to test the state of the `Activity` against before running the check.\n\nIf any set of criteria passes the Check will be run. If the criteria fails then the Check will fail.\n\n* @examples [[{\"over_18\": true, \"removed': false}]]"

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@ import {
generateFullWikiUrl,
generateItemFilterHelpers,
getActivityAuthorName,
getActivitySubredditName, humanizeBanDetails,
getActivitySubredditName,
isComment,
isCommentState,
isRuleSetResult,
@@ -56,7 +56,7 @@ import {
} from "../util";
import {
ActivityDispatch,
CacheConfig, CacheOptions,
CacheConfig,
Footer,
HistoricalStatsDisplay,
ResourceStats, StrongTTLConfig,
@@ -88,7 +88,7 @@ import cloneDeep from "lodash/cloneDeep";
import {
asModLogCriteria,
asModNoteCriteria,
AuthorCriteria, BanCriteria,
AuthorCriteria,
cmToSnoowrapActivityMap, cmToSnoowrapAuthorMap,
CommentState,
ModLogCriteria,
@@ -104,7 +104,7 @@ import {
UserNoteCriteria
} from "../Common/Infrastructure/Filters/FilterCriteria";
import {
ActivitySourceValue, asSubredditPlaceholder,
ActivitySourceValue,
ConfigFragmentParseFunc,
DurationVal,
EventRetentionPolicyRange,
@@ -115,7 +115,7 @@ import {
ModUserNoteLabel,
RelativeDateTimeMatch,
statFrequencies,
StatisticFrequencyOption, SubredditPlaceholderType,
StatisticFrequencyOption,
WikiContext
} from "../Common/Infrastructure/Atomic";
import {
@@ -136,9 +136,9 @@ import {Duration} from "dayjs/plugin/duration";
import {
ActivityType,
AuthorHistorySort,
CachedFetchedActivitiesResult, CMBannedUser,
CachedFetchedActivitiesResult,
FetchedActivitiesResult, MaybeActivityType, RedditUserLike,
SnoowrapActivity, SnoowrapBannedUser,
SnoowrapActivity,
SubredditLike,
SubredditRemovalReason
} from "../Common/Infrastructure/Reddit";
@@ -161,9 +161,7 @@ import {ActionResultEntity} from "../Common/Entities/ActionResultEntity";
import {ActivitySource} from "../Common/ActivitySource";
import {SubredditResourceOptions} from "../Common/Subreddit/SubredditResourceInterfaces";
import {SubredditStats} from "./Stats";
import {CMCache, createCacheManager} from "../Common/Cache";
import {BannedUser, BanOptions} from "snoowrap/dist/objects/Subreddit";
import {testBanCriteria} from "../Utils/Criteria/AuthorCritUtils";
import {CMCache} from "../Common/Cache";
export const DEFAULT_FOOTER = '\r\n*****\r\nThis action was performed by [a bot.]({{botLink}}) Mention a moderator or [send a modmail]({{modmailLink}}) if you have any ideas, questions, or concerns about this action.';
@@ -189,7 +187,6 @@ export class SubredditResources {
database: DataSource
client: ExtendedSnoowrap
cache: CMCache
memoryCache: CMCache
cacheSettingsHash?: string;
thirdPartyCredentials: ThirdPartyCredentialsJsonConfig;
delayedItems: ActivityDispatch[] = [];
@@ -247,12 +244,6 @@ export class SubredditResources {
}
this.cache = cache;
this.cache.setLogger(this.logger);
const memoryCacheOpts: CacheOptions = {
store: 'memory',
max: 10,
ttl: 10
};
this.memoryCache = new CMCache(createCacheManager(memoryCacheOpts), memoryCacheOpts, false, undefined, {}, this.logger);
this.subredditStats = new SubredditStats(database, managerEntity, cache, statFrequency, this.logger);
@@ -870,47 +861,6 @@ export class SubredditResources {
}
}
async getSubredditBannedUser(val: string | RedditUser): Promise<CMBannedUser | undefined> {
const subName = this.subreddit.display_name;
const name = getActivityAuthorName(val);
const hash = `sub-${subName}-banned-${name}`;
if (this.ttl.authorTTL !== false) {
const cachedBanData = (await this.cache.get(hash)) as undefined | null | false | SnoowrapBannedUser;
if (cachedBanData !== undefined && cachedBanData !== null) {
this.logger.debug(`Cache Hit: Subreddit Banned User ${subName} ${name}`);
if(cachedBanData === false) {
return undefined;
}
return {...cachedBanData, date: dayjs.unix(cachedBanData.date), days_left: cachedBanData.days_left === null ? undefined : dayjs.duration({days: cachedBanData.days_left}), user: new RedditUser({name: cachedBanData.name}, this.client, false)};
}
}
let bannedUsers = await this.subreddit.getBannedUsers({name});
let bannedUser: CMBannedUser | undefined;
if(bannedUsers.length > 0) {
const banData = bannedUsers[0] as SnoowrapBannedUser;
bannedUser = {...banData, date: dayjs.unix(banData.date), days_left: banData.days_left === null ? undefined : dayjs.duration({days: banData.days_left}), user: new RedditUser({name: banData.name}, this.client, false)};
}
if (this.ttl.authorTTL !== false) {
// @ts-ignore
await this.cache.set(hash, bannedUsers.length > 0 ? bannedUsers[0] as SnoowrapBannedUser : false, {ttl: this.ttl.subredditTTL});
}
return bannedUser;
}
async addUserToSubredditBannedUserCache(data: BanOptions) {
if (this.ttl.authorTTL !== false) {
const subName = this.subreddit.display_name;
const name = getActivityAuthorName(data.name);
const hash = `sub-${subName}-banned-${name}`;
const banData: SnoowrapBannedUser = {date: dayjs().unix(), name: data.name, days_left: data.duration ?? null, note: data.banNote ?? ''};
await this.cache.set(hash, banData, {ttl: this.ttl.authorTTL})
}
}
async hasSubreddit(name: string) {
if (this.ttl.subredditTTL !== false) {
const hash = `sub-${name}`;
@@ -1130,9 +1080,6 @@ export class SubredditResources {
async getActivities(user: RedditUser, options: ActivityWindowCriteria, listingData: NamedListing, prefetchedActivities: SnoowrapActivity[] = []): Promise<FetchedActivitiesResult> {
let cacheKey: string | undefined;
let fromCache = false;
try {
let pre: SnoowrapActivity[] = [];
@@ -1140,6 +1087,7 @@ export class SubredditResources {
let apiCount = 1;
let preMaxTrigger: undefined | string;
let rawCount: number = 0;
let fromCache = false;
const hashObj = cloneDeep(options);
@@ -1152,23 +1100,13 @@ export class SubredditResources {
const userName = getActivityAuthorName(user);
const hash = objectHash.sha1(hashObj);
cacheKey = `${userName}-${listingData.name}-${hash}`;
const cacheKey = `${userName}-${listingData.name}-${hash}`;
if (this.ttl.authorTTL !== false) {
if (this.useSubredditAuthorCache) {
hashObj.subreddit = this.subreddit;
}
// check for cached request error!
//
// we cache reddit API request errors for 403/404 (suspended/shadowban) in memory so that
// we don't waste API calls making the same call repetitively since we know what the result will always be
const cachedRequestError = await this.memoryCache.get(cacheKey) as undefined | null | Error;
if(cachedRequestError !== undefined && cachedRequestError !== null) {
fromCache = true;
this.logger.debug(`In-memory cache found reddit request error for key ${cacheKey}. Must have been <5 sec ago. Throwing to save API calls!`);
throw cachedRequestError;
}
const cacheVal = await this.cache.get(cacheKey);
if(cacheVal === undefined || cacheVal === null) {
@@ -1405,14 +1343,9 @@ export class SubredditResources {
} catch (err: any) {
if(isStatusError(err)) {
switch(err.statusCode) {
case 403:
case 404:
if(!fromCache && cacheKey !== undefined) {
await this.memoryCache.set(cacheKey, err, {ttl: 5});
}
if(err.statusCode === 404) {
throw new SimpleError('Reddit returned a 404 for user history. Likely this user is shadowbanned.', {isSerious: false});
}
throw new SimpleError('Reddit returned a 404 for user history. Likely this user is shadowbanned.', {isSerious: false});
case 403:
throw new MaybeSeriousErrorWithCause('Reddit returned a 403 for user history, likely this user is suspended.', {cause: err, isSerious: false});
default:
throw err;
@@ -1958,14 +1891,8 @@ export class SubredditResources {
if (crit[k] !== undefined) {
switch (k) {
case 'name':
const nameReg = crit[k] as RegExp | SubredditPlaceholderType;
// placeholder {{subreddit}} tests as true if the given subreddit matches the subreddit this bot is processing the activity from
if (asSubredditPlaceholder(nameReg)) {
if (this.subreddit.display_name !== subreddit.display_name) {
log.debug(`Failed: Expected => ${k}:${crit[k]} (${this.subreddit.display_name}) | Found => ${k}:${subreddit.display_name}`)
return false
}
} else if (!nameReg.test(subreddit.display_name)) {
const nameReg = crit[k] as RegExp;
if(!nameReg.test(subreddit.display_name)) {
return false;
}
break;
@@ -2788,33 +2715,6 @@ export class SubredditResources {
shouldContinue = false;
}
break;
case 'banned':
const banDetails = await this.getSubredditBannedUser(item.author);
const isBanned = banDetails !== undefined;
propResultsMap.banned!.found = humanizeBanDetails(banDetails);
if(typeof authorOpts.banned === 'boolean') {
propResultsMap.banned!.passed = criteriaPassWithIncludeBehavior(isBanned === authorOpts.banned, include);
} else if(!isBanned) {
// since banned criteria is not boolean it must be criteria(s)
// and if user is not banned then no criteria will pass
propResultsMap.banned!.passed = criteriaPassWithIncludeBehavior(false, include);
} else {
const bCritVal = authorOpts.banned as BanCriteria | BanCriteria[];
const bCritArr = !Array.isArray(bCritVal) ? [bCritVal] : bCritVal;
let anyBanCritPassed = false;
for(const bCrit of bCritArr) {
anyBanCritPassed = testBanCriteria(bCrit, banDetails);
if(anyBanCritPassed) {
break;
}
}
propResultsMap.banned!.passed = criteriaPassWithIncludeBehavior(anyBanCritPassed, include);
}
if (!propResultsMap.banned!.passed) {
shouldContinue = false;
}
break;
case 'userNotes':
const unCriterias = (authorOpts[k] as UserNoteCriteria[]).map(x => toFullUserNoteCriteria(x));
const notes = await this.userNotes.getUserNotes(item.author);

View File

@@ -1,79 +0,0 @@
import {BanCriteria} from "../../Common/Infrastructure/Filters/FilterCriteria";
import {boolToString, testMaybeStringRegex} from "../../util";
import {CMBannedUser} from "../../Common/Infrastructure/Reddit";
import {compareDurationValue, parseDurationComparison} from "../../Common/Infrastructure/Comparisons";
import dayjs from "dayjs";
export const humanizeBanCriteria = (crit: BanCriteria): string => {
const parts: string[] = [];
for (const [k, v] of Object.entries(crit)) {
switch (k.toLowerCase()) {
case 'note':
parts.push(`has notes matching: "${Array.isArray(v) ? v.join(' || ') : v}"`);
break;
default:
parts.push(`${k}: ${typeof v === 'boolean' ? boolToString(v) : v.toString()}`);
break;
}
}
return parts.join(' AND ');
}
export const testBanCriteria = (crit: BanCriteria, banUser: CMBannedUser): boolean => {
if (crit.permanent !== undefined) {
// easiest to test for
if ((banUser.days_left === undefined && !crit.permanent) || (banUser.days_left !== undefined && crit.permanent)) {
return false;
}
}
if (crit.note !== undefined) {
let anyPassed = false;
const expectedValues = Array.isArray(crit.note) ? crit.note : [crit.note];
for (const expectedVal of expectedValues) {
try {
const [regPassed] = testMaybeStringRegex(expectedVal, banUser.note);
if (regPassed) {
anyPassed = true;
}
} catch (err: any) {
if (err.message.includes('Could not convert test value')) {
// fallback to simple comparison
anyPassed = expectedVal.toLowerCase() === banUser.note.toLowerCase();
} else {
throw err;
}
}
if (anyPassed) {
break;
}
}
if (!anyPassed) {
return false;
}
}
if (crit.bannedAt !== undefined) {
const ageTest = compareDurationValue(parseDurationComparison(crit.bannedAt), banUser.date);
if (!ageTest) {
return false;
}
}
if (crit.daysLeft !== undefined) {
const daysLeftCompare = parseDurationComparison(crit.daysLeft);
if (banUser.days_left === undefined) {
if (daysLeftCompare.operator.includes('<')) {
// permaban, will never be less than some finite duration
return false;
}
// otherwise will always pass since any finite duration is less than infinity
} else {
const dayTest = compareDurationValue(daysLeftCompare, dayjs().add(banUser.days_left));
if (!dayTest) {
return false;
}
}
}
return true;
}

View File

@@ -46,6 +46,83 @@ import {ActionResultEntity} from "../Common/Entities/ActionResultEntity";
export const BOT_LINK = 'https://www.reddit.com/r/ContextModBot/comments/otz396/introduction_to_contextmodbot';
export interface AuthorTypedActivitiesOptions extends ActivityWindowCriteria {
type?: 'comment' | 'submission',
}
export const isSubreddit = async (subreddit: Subreddit, stateCriteria: SubredditCriteria | StrongSubredditCriteria, logger?: Logger) => {
delete stateCriteria.stateDescription;
if (Object.keys(stateCriteria).length === 0) {
return true;
}
const crit = isStrongSubredditState(stateCriteria) ? stateCriteria : toStrongSubredditState(stateCriteria, {defaultFlags: 'i'});
const log: Logger | undefined = logger !== undefined ? logger.child({leaf: 'Subreddit Check'}, mergeArr) : undefined;
return await (async () => {
for (const k of Object.keys(crit)) {
// @ts-ignore
if (crit[k] !== undefined) {
switch (k) {
case 'name':
const nameReg = crit[k] as RegExp;
if(!nameReg.test(subreddit.display_name)) {
return false;
}
break;
case 'isUserProfile':
const entity = parseRedditEntity(subreddit.display_name);
const entityIsUserProfile = entity.type === 'user';
if(crit[k] !== entityIsUserProfile) {
if(log !== undefined) {
log.debug(`Failed: Expected => ${k}:${crit[k]} | Found => ${k}:${entityIsUserProfile}`)
}
return false
}
break;
case 'over18':
case 'over_18':
// handling an edge case where user may have confused Comment/Submission state "over_18" with SubredditState "over18"
// @ts-ignore
if (crit[k] !== subreddit.over18) {
if(log !== undefined) {
// @ts-ignore
log.debug(`Failed: Expected => ${k}:${crit[k]} | Found => ${k}:${subreddit.over18}`)
}
return false
}
break;
default:
// @ts-ignore
if (subreddit[k] !== undefined) {
// @ts-ignore
if (crit[k] !== subreddit[k]) {
if(log !== undefined) {
// @ts-ignore
log.debug(`Failed: Expected => ${k}:${crit[k]} | Found => ${k}:${subreddit[k]}`)
}
return false
}
} else {
if(log !== undefined) {
log.warn(`Tried to test for Subreddit property '${k}' but it did not exist`);
}
}
break;
}
}
}
if(log !== undefined) {
log.debug(`Passed: ${JSON.stringify(stateCriteria)}`);
}
return true;
})() as boolean;
}
const renderContentCommentTruncate = truncateStringToLength(50);
const shortTitleTruncate = truncateStringToLength(15);
@@ -100,7 +177,6 @@ export const renderContent = async (template: string, data: TemplateContext = {}
conditional.spoiler = activity.spoiler;
conditional.op = true;
conditional.upvoteRatio = `${activity.upvote_ratio * 100}%`;
conditional.link_flair_text = activity.link_flair_text;
} else {
conditional.op = activity.is_submitter;
}
@@ -123,7 +199,6 @@ export const renderContent = async (template: string, data: TemplateContext = {}
author.commentKarma = auth.comment_karma;
author.totalKarma = auth.comment_karma + auth.link_karma;
author.verified = auth.has_verified_email;
author.flairText = activity.author_flair_text;
}
const templateData: any = {

View File

@@ -12,7 +12,6 @@ import {Logger} from "winston";
import {WebSetting} from "../../Common/WebEntities/WebSetting";
import {ErrorWithCause} from "pony-cause";
import {createCacheManager} from "../../Common/Cache";
import {MysqlDriver} from "typeorm/driver/mysql/MysqlDriver";
export interface CacheManagerStoreOptions {
prefix?: string
@@ -104,12 +103,7 @@ export class DatabaseStorageProvider extends StorageProvider {
}
createSessionStore(options?: TypeormStoreOptions): Store {
// https://github.com/freshgiammi-lab/connect-typeorm#implement-the-session-entity
// https://github.com/freshgiammi-lab/connect-typeorm/issues/8
// usage of LIMIT in subquery is not supported by mariadb/mysql
// limitSubquery: false -- turns off LIMIT usage
const realOptions = this.database.driver instanceof MysqlDriver ? {...options, limitSubquery: false} : options;
return new TypeormStore(realOptions).connect(this.clientSessionRepo)
return new TypeormStore(options).connect(this.clientSessionRepo)
}
async getSessionSecret(): Promise<string | undefined> {

View File

@@ -46,14 +46,7 @@ import {ErrorWithCause, stackWithCauses} from "pony-cause";
import stringSimilarity from 'string-similarity';
import calculateCosineSimilarity from "./Utils/StringMatching/CosineSimilarity";
import levenSimilarity from "./Utils/StringMatching/levenSimilarity";
import {
isRateLimitError,
isRequestError,
isScopeError,
isSeriousError,
isStatusError,
SimpleError
} from "./Utils/Errors";
import {isRateLimitError, isRequestError, isScopeError, isStatusError, SimpleError} from "./Utils/Errors";
import merge from "deepmerge";
import {RulePremise} from "./Common/Entities/RulePremise";
import {RuleResultEntity as RuleResultEntity} from "./Common/Entities/RuleResultEntity";
@@ -77,7 +70,7 @@ import {
import {
ActivitySourceData,
ActivitySourceTypes,
ActivitySourceValue, asSubredditPlaceholder,
ActivitySourceValue,
ConfigFormat,
DurationVal,
ExternalUrlContext,
@@ -90,7 +83,7 @@ import {
RelativeDateTimeMatch,
statFrequencies,
StatisticFrequency,
StatisticFrequencyOption, subredditPlaceholder, SubredditPlaceholderType,
StatisticFrequencyOption,
UrlContext,
WikiContext
} from "./Common/Infrastructure/Atomic";
@@ -111,7 +104,7 @@ import {
} from "./Common/Infrastructure/Filters/FilterShapes";
import {
ActivityType,
AuthorHistoryType, CMBannedUser,
AuthorHistoryType,
FullNameTypes,
PermalinkRedditThings,
RedditThing,
@@ -1103,22 +1096,16 @@ export const createRetryHandler = (opts: RetryOptions, logger: Logger) => {
// if it's a request error but not a known "oh probably just a reddit blip" status code treat it as other, which should usually have a lower retry max
}
let prefix = '';
if(isSeriousError(err)) {
// linear backoff
otherRetryCount++;
} else {
prefix = 'NON-SERIOUS ';
}
// linear backoff
otherRetryCount++;
let msg = redditApiError ? `Error occurred while making a request to Reddit (${otherRetryCount}/${maxOtherRetry} in ${clearRetryCountAfter} minutes) but it was NOT a well-known "reddit blip" error.` : `Non-request error occurred (${otherRetryCount}/${maxOtherRetry} in ${clearRetryCountAfter} minutes).`;
if (maxOtherRetry < otherRetryCount) {
logger.warn(`${prefix}${msg} Exceeded max allowed.`);
logger.warn(`${msg} Exceeded max allowed.`);
return false;
}
if(waitOnRetry) {
const ms = (4 * 1000) * otherRetryCount;
logger.warn(`${prefix}${msg} Will wait ${formatNumber(ms / 1000)} seconds before retrying`);
logger.warn(`${msg} Will wait ${formatNumber(ms / 1000)} seconds before retrying`);
await sleep(ms);
}
return true;
@@ -1570,7 +1557,7 @@ export const testMaybeStringRegex = (test: string, subject: string, defaultFlags
}
export const isStrongSubredditState = (value: SubredditCriteria | StrongSubredditCriteria) => {
return value.name === undefined || value.name instanceof RegExp || asSubredditPlaceholder(value.name);
return value.name === undefined || value.name instanceof RegExp;
}
export const asStrongSubredditState = (value: any): value is StrongSubredditCriteria => {
@@ -1588,26 +1575,21 @@ export const toStrongSubredditState = (s: SubredditCriteria, opts?: StrongSubred
let nameValOriginallyRegex = false;
let nameReg: RegExp | undefined | SubredditPlaceholderType;
let nameReg: RegExp | undefined;
if (nameValRaw !== undefined) {
if (!(nameValRaw instanceof RegExp)) {
let nameVal = nameValRaw.trim();
if(asSubredditPlaceholder(nameVal)) {
nameReg = subredditPlaceholder;
nameValOriginallyRegex = false;
nameReg = parseStringToRegex(nameVal, defaultFlags);
if (nameReg === undefined) {
// if sub state has `isUserProfile=true` and config did not provide a regex then
// assume the user wants to use the value in "name" to look for a user profile so we prefix created regex with u_
const parsedEntity = parseRedditEntity(nameVal, isUserProfile !== undefined && isUserProfile ? 'user' : 'subreddit');
// technically they could provide "u_Username" as the value for "name" and we will then match on it regardless of isUserProfile
// but like...why would they do that? There shouldn't be any subreddits that start with u_ that aren't user profiles anyway(?)
const regPrefix = parsedEntity.type === 'user' ? 'u_' : '';
nameReg = parseStringToRegex(`/^${regPrefix}${nameVal}$/`, defaultFlags);
} else {
nameReg = parseStringToRegex(nameVal, defaultFlags);
if (nameReg === undefined) {
// if sub state has `isUserProfile=true` and config did not provide a regex then
// assume the user wants to use the value in "name" to look for a user profile so we prefix created regex with u_
const parsedEntity = parseRedditEntity(nameVal, isUserProfile !== undefined && isUserProfile ? 'user' : 'subreddit');
// technically they could provide "u_Username" as the value for "name" and we will then match on it regardless of isUserProfile
// but like...why would they do that? There shouldn't be any subreddits that start with u_ that aren't user profiles anyway(?)
const regPrefix = parsedEntity.type === 'user' ? 'u_' : '';
nameReg = parseStringToRegex(`/^${regPrefix}${nameVal}$/`, defaultFlags);
} else {
nameValOriginallyRegex = true;
}
nameValOriginallyRegex = true;
}
} else {
nameValOriginallyRegex = true;
@@ -3116,15 +3098,3 @@ export const toPollOn = (val: string | PollOn): PollOn => {
}
throw new SimpleError(`'${val}' is not a valid polling source. Valid sources: ${pollOnTypes.join(' | ')}`);
}
export const humanizeBanDetails = (banDetails: CMBannedUser | undefined): string => {
if(banDetails === undefined) {
return 'Not Banned';
}
const timeSinceBan = dayjs.duration(dayjs().diff(banDetails.date)).humanize(true);
if(banDetails.days_left === undefined) {
return `Banned permanently ${timeSinceBan}`;
}
return `Banned ${timeSinceBan} with ${banDetails.days_left?.days()} days left`;
}