mirror of
https://github.com/FoxxMD/context-mod.git
synced 2026-04-19 03:00:07 -04:00
Refactor json parsing to use ajv for all validation instead of guards
* Generate individual schemas for root objects from config (Rule, RuleSet, Action) * Using ajv validation means we can also report all validation errors * Remove noisy generated guard files
This commit is contained in:
@@ -8,7 +8,11 @@
|
||||
"build": "tsc",
|
||||
"start": "node server.js",
|
||||
"guard": "ts-auto-guard src/JsonConfig.ts",
|
||||
"schema": "typescript-json-schema tsconfig.json JSONConfig --out src/Schema/schema.json --required --tsNodeRegister --refs --propOrder",
|
||||
"schema": "npm run -s schema-app & npm run -s schema-ruleset & npm run -s schema-rule & npm run -s schema-action",
|
||||
"schema-app": "typescript-json-schema tsconfig.json JSONConfig --out src/Schema/App.json --required --tsNodeRegister --refs --propOrder",
|
||||
"schema-ruleset": "typescript-json-schema tsconfig.json RuleSetJSONConfig --out src/Schema/RuleSet.json --required --tsNodeRegister --refs --propOrder",
|
||||
"schema-rule": "typescript-json-schema tsconfig.json RuleJSONConfig --out src/Schema/Rule.json --required --tsNodeRegister --refs --propOrder",
|
||||
"schema-action": "typescript-json-schema tsconfig.json ActionJSONConfig --out src/Schema/Action.json --required --tsNodeRegister --refs --propOrder",
|
||||
"schemaNotWorking": "./node_modules/.bin/ts-json-schema-generator -f tsconfig.json -p src/JsonConfig.ts -t JSONConfig --out src/Schema/vegaSchema.json"
|
||||
},
|
||||
"keywords": [],
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=Action.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Action.guard.js","sourceRoot":"","sources":["Action.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=CommentAction.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommentAction.guard.js","sourceRoot":"","sources":["CommentAction.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=LockAction.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"LockAction.guard.js","sourceRoot":"","sources":["LockAction.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=RemoveAction.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"RemoveAction.guard.js","sourceRoot":"","sources":["RemoveAction.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=ReportAction.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"ReportAction.guard.js","sourceRoot":"","sources":["ReportAction.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=FlairAction.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"FlairAction.guard.js","sourceRoot":"","sources":["FlairAction.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=index.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.guard.js","sourceRoot":"","sources":["index.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Generated type guards for "index.ts".
|
||||
* WARNING: Do not manually change this file.
|
||||
*/
|
||||
import { ActionJSONConfig } from "./index";
|
||||
|
||||
export function isActionConfig(obj: any, _argumentName?: string): obj is ActionJSONConfig {
|
||||
return (
|
||||
(obj !== null &&
|
||||
typeof obj === "object" ||
|
||||
typeof obj === "function") &&
|
||||
(typeof obj.name === "undefined" ||
|
||||
typeof obj.name === "string") &&
|
||||
(obj.kind === "comment" ||
|
||||
obj.kind === "lock" ||
|
||||
obj.kind === "remove" ||
|
||||
obj.kind === "report" ||
|
||||
obj.kind === "flair")
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Snoowrap, {Comment, Submission} from "snoowrap";
|
||||
import {Logger} from "winston";
|
||||
import {createLabelledLogger} from "../util";
|
||||
import {createLabelledLogger, loggerMetaShuffle} from "../util";
|
||||
|
||||
export abstract class Action {
|
||||
name?: string;
|
||||
@@ -19,7 +19,7 @@ export abstract class Action {
|
||||
const prefix = `${loggerPrefix}|${this.name}`;
|
||||
this.logger = createLabelledLogger(prefix, prefix);
|
||||
} else {
|
||||
this.logger = logger;
|
||||
this.logger = logger.child(loggerMetaShuffle(logger, name || 'Action', undefined, {truncateLength: 100}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=CommentCheck.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommentCheck.guard.js","sourceRoot":"","sources":["CommentCheck.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=SubmissionCheck.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SubmissionCheck.guard.js","sourceRoot":"","sources":["SubmissionCheck.guard.ts"],"names":[],"mappings":""}
|
||||
@@ -1,80 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isCheckConfig = void 0;
|
||||
/*
|
||||
* Generated type guards for "index.ts".
|
||||
* WARNING: Do not manually change this file.
|
||||
*/
|
||||
const index_guard_1 = require("../Rule/index.guard");
|
||||
function isCheckConfig(obj, _argumentName) {
|
||||
return ((obj !== null &&
|
||||
typeof obj === "object" ||
|
||||
typeof obj === "function") &&
|
||||
typeof obj.name === "string" &&
|
||||
(typeof obj.description === "undefined" ||
|
||||
typeof obj.description === "string") &&
|
||||
(typeof obj.ruleJoin === "undefined" ||
|
||||
obj.ruleJoin === "OR" ||
|
||||
obj.ruleJoin === "AND") &&
|
||||
Array.isArray(obj.rules) &&
|
||||
obj.rules.every((e) => (index_guard_1.isRuleConfig(e) ||
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.condition === "OR" ||
|
||||
e.condition === "AND") &&
|
||||
Array.isArray(e.rules) &&
|
||||
e.rules.every((e) => (e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.authors !== null &&
|
||||
typeof e.authors === "object" ||
|
||||
typeof e.authors === "function") &&
|
||||
(typeof e.authors.exclude === "undefined" ||
|
||||
Array.isArray(e.authors.exclude) &&
|
||||
e.authors.exclude.every((e) => (e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e) => typeof e === "string")) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e) => typeof e === "string")) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e) => typeof e === "string")) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true))) &&
|
||||
(typeof e.authors.include === "undefined" ||
|
||||
Array.isArray(e.authors.include) &&
|
||||
e.authors.include.every((e) => (e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e) => typeof e === "string")) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e) => typeof e === "string")) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e) => typeof e === "string")) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)))) &&
|
||||
Array.isArray(e.rules) &&
|
||||
e.rules.every((e) => index_guard_1.isRuleConfig(e)))) &&
|
||||
Array.isArray(obj.actions) &&
|
||||
obj.actions.every((e) => (e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.kind === "comment" ||
|
||||
e.kind === "lock" ||
|
||||
e.kind === "remove" ||
|
||||
e.kind === "report" ||
|
||||
e.kind === "flair")));
|
||||
}
|
||||
exports.isCheckConfig = isCheckConfig;
|
||||
//# sourceMappingURL=index.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.guard.js","sourceRoot":"","sources":["index.guard.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Generated type guards for "index.ts".
|
||||
* WARNING: Do not manually change this file.
|
||||
*/
|
||||
import { isRuleConfig } from "../Rule/index.guard";
|
||||
import { CheckJSONConfig } from "./index";
|
||||
|
||||
export function isCheckConfig(obj: any, _argumentName?: string): obj is CheckJSONConfig {
|
||||
return (
|
||||
(obj !== null &&
|
||||
typeof obj === "object" ||
|
||||
typeof obj === "function") &&
|
||||
typeof obj.name === "string" &&
|
||||
(typeof obj.description === "undefined" ||
|
||||
typeof obj.description === "string") &&
|
||||
(typeof obj.ruleJoin === "undefined" ||
|
||||
obj.ruleJoin === "OR" ||
|
||||
obj.ruleJoin === "AND") &&
|
||||
Array.isArray(obj.rules) &&
|
||||
obj.rules.every((e: any) =>
|
||||
(isRuleConfig(e) as boolean ||
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.condition === "OR" ||
|
||||
e.condition === "AND") &&
|
||||
Array.isArray(e.rules) &&
|
||||
e.rules.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.authors !== null &&
|
||||
typeof e.authors === "object" ||
|
||||
typeof e.authors === "function") &&
|
||||
(typeof e.authors.exclude === "undefined" ||
|
||||
Array.isArray(e.authors.exclude) &&
|
||||
e.authors.exclude.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
)) &&
|
||||
(typeof e.authors.include === "undefined" ||
|
||||
Array.isArray(e.authors.include) &&
|
||||
e.authors.include.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
))
|
||||
) &&
|
||||
Array.isArray(e.rules) &&
|
||||
e.rules.every((e: any) =>
|
||||
isRuleConfig(e) as boolean
|
||||
))
|
||||
) &&
|
||||
Array.isArray(obj.actions) &&
|
||||
obj.actions.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e.kind === "comment" ||
|
||||
e.kind === "lock" ||
|
||||
e.kind === "remove" ||
|
||||
e.kind === "report" ||
|
||||
e.kind === "flair")
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -18,6 +18,12 @@ import {ReportActionJSONConfig} from "../Action/ReportAction";
|
||||
import {LockActionJSONConfig} from "../Action/LockAction";
|
||||
import {RemoveActionJSONConfig} from "../Action/RemoveAction";
|
||||
import {JoinCondition, JoinOperands} from "../Common/interfaces";
|
||||
import * as RuleSchema from '../Schema/Rule.json';
|
||||
import * as RuleSetSchema from '../Schema/RuleSet.json';
|
||||
import * as ActionSchema from '../Schema/Action.json';
|
||||
import Ajv from 'ajv';
|
||||
|
||||
const ajv = new Ajv();
|
||||
|
||||
export class Check implements ICheck {
|
||||
actions: Action[] = [];
|
||||
@@ -49,20 +55,46 @@ export class Check implements ICheck {
|
||||
for (const r of rules) {
|
||||
if (r instanceof Rule || r instanceof RuleSet) {
|
||||
this.rules.push(r);
|
||||
} else if (isRuleSetConfig(r)) {
|
||||
this.rules.push(new RuleSet(r));
|
||||
} else if (isRuleConfig(r)) {
|
||||
// @ts-ignore
|
||||
r.logger = this.logger;
|
||||
this.rules.push(ruleFactory(r));
|
||||
} else {
|
||||
let valid = ajv.validate(RuleSetSchema, r);
|
||||
let setErrors: any = [];
|
||||
let ruleErrors: any = [];
|
||||
if (valid) {
|
||||
// @ts-ignore
|
||||
r.logger = this.logger;
|
||||
this.rules.push(new RuleSet(r as RuleSetJSONConfig));
|
||||
} else {
|
||||
setErrors = ajv.errors;
|
||||
valid = ajv.validate(RuleSchema, r);
|
||||
if (valid) {
|
||||
// @ts-ignore
|
||||
r.logger = this.logger;
|
||||
this.rules.push(ruleFactory(r as RuleJSONConfig));
|
||||
} else {
|
||||
ruleErrors = ajv.errors;
|
||||
const leastErrorType = setErrors.length < ruleErrors ? 'RuleSet' : 'Rule';
|
||||
const errors = setErrors.length < ruleErrors ? setErrors : ruleErrors;
|
||||
this.logger.warn(`Could not parse object as RuleSet or Rule json. ${leastErrorType} validation had least errors`, {}, {
|
||||
errors,
|
||||
obj: r
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const a of actions) {
|
||||
if (a instanceof Action) {
|
||||
this.actions.push(a);
|
||||
} else if (isActionConfig(a)) {
|
||||
this.actions.push(actionFactory(a));
|
||||
} else {
|
||||
let valid = ajv.validate(ActionSchema, a);
|
||||
if (valid) {
|
||||
this.actions.push(actionFactory(a as ActionJSONConfig));
|
||||
// @ts-ignore
|
||||
a.logger = this.logger;
|
||||
} else {
|
||||
this.logger.warn('Could not parse object as Action', {}, {error: ajv.errors, obj: a})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,9 +147,8 @@ export interface PollingOptions {
|
||||
* */
|
||||
limit?: number,
|
||||
/**
|
||||
* Amount of time to wait between requests to /r/subreddit/new
|
||||
* Amount of time, in milliseconds, to wait between requests to /r/subreddit/new
|
||||
*
|
||||
* @format milliseconds
|
||||
* @default 10000
|
||||
* */
|
||||
interval?: number,
|
||||
@@ -164,9 +163,8 @@ export interface PollingOptions {
|
||||
* */
|
||||
limit?: number,
|
||||
/**
|
||||
* Amount of time to wait between requests for new comments
|
||||
* Amount of time, in milliseconds, to wait between requests for new comments
|
||||
*
|
||||
* @format milliseconds
|
||||
* @default 10000
|
||||
* */
|
||||
interval?: number,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {Logger} from "winston";
|
||||
import {createLabelledLogger, loggerMetaShuffle, mergeArr} from "./util";
|
||||
import {Subreddit} from "snoowrap";
|
||||
import {CommentCheck} from "./Check/CommentCheck";
|
||||
import {SubmissionCheck} from "./Check/SubmissionCheck";
|
||||
|
||||
import Ajv from 'ajv';
|
||||
import * as schema from './Schema/schema.json';
|
||||
import * as schema from './Schema/App.json';
|
||||
import {JSONConfig} from "./JsonConfig";
|
||||
import LoggedError from "./Utils/LoggedError";
|
||||
|
||||
@@ -13,15 +12,12 @@ const ajv = new Ajv();
|
||||
|
||||
export interface ConfigBuilderOptions {
|
||||
logger?: Logger,
|
||||
//subreddit: Subreddit,
|
||||
}
|
||||
|
||||
export class ConfigBuilder {
|
||||
logger: Logger;
|
||||
//subreddit: Subreddit;
|
||||
|
||||
constructor(options: ConfigBuilderOptions) {
|
||||
// this.subreddit = options.subreddit;
|
||||
|
||||
if (options.logger !== undefined) {
|
||||
this.logger = options.logger.child(loggerMetaShuffle(options.logger, 'Config'), mergeArr);
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Generated type guards for "RuleSet.ts".
|
||||
* WARNING: Do not manually change this file.
|
||||
*/
|
||||
import { RuleSetJSONConfig } from "./RuleSet";
|
||||
|
||||
export function isRuleSetConfig(obj: any, _argumentName?: string): obj is RuleSetJSONConfig {
|
||||
return (
|
||||
(obj !== null &&
|
||||
typeof obj === "object" ||
|
||||
typeof obj === "function") &&
|
||||
(obj.condition === "OR" ||
|
||||
obj.condition === "AND") &&
|
||||
Array.isArray(obj.rules) &&
|
||||
obj.rules.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
typeof e.name === "string") &&
|
||||
(typeof e.authors === "undefined" ||
|
||||
(e.authors !== null &&
|
||||
typeof e.authors === "object" ||
|
||||
typeof e.authors === "function") &&
|
||||
(typeof e.authors.exclude === "undefined" ||
|
||||
Array.isArray(e.authors.exclude) &&
|
||||
e.authors.exclude.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
)) &&
|
||||
(typeof e.authors.include === "undefined" ||
|
||||
Array.isArray(e.authors.include) &&
|
||||
e.authors.include.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
)))
|
||||
) &&
|
||||
Array.isArray(obj.rules) &&
|
||||
obj.rules.every((e: any) =>
|
||||
((e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.window === "undefined" ||
|
||||
typeof e.window === "string" ||
|
||||
typeof e.window === "number") &&
|
||||
(typeof e.usePostAsReference === "undefined" ||
|
||||
e.usePostAsReference === false ||
|
||||
e.usePostAsReference === true) &&
|
||||
(typeof e.lookAt === "undefined" ||
|
||||
e.lookAt === "comments" ||
|
||||
e.lookAt === "submissions") &&
|
||||
Array.isArray(e.thresholds) &&
|
||||
e.thresholds.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
Array.isArray(e.subreddits) &&
|
||||
e.subreddits.every((e: any) =>
|
||||
typeof e === "string"
|
||||
) &&
|
||||
(typeof e.count === "undefined" ||
|
||||
typeof e.count === "number")
|
||||
) &&
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
typeof e.name === "string") &&
|
||||
(typeof e.authors === "undefined" ||
|
||||
(e.authors !== null &&
|
||||
typeof e.authors === "object" ||
|
||||
typeof e.authors === "function") &&
|
||||
(typeof e.authors.exclude === "undefined" ||
|
||||
Array.isArray(e.authors.exclude) &&
|
||||
e.authors.exclude.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
)) &&
|
||||
(typeof e.authors.include === "undefined" ||
|
||||
Array.isArray(e.authors.include) &&
|
||||
e.authors.include.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
))) &&
|
||||
(e.kind === "recentActivity" ||
|
||||
e.kind === "repeatSubmission" ||
|
||||
e.kind === "author") ||
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
typeof e.threshold === "number" &&
|
||||
(typeof e.window === "undefined" ||
|
||||
typeof e.window === "string" ||
|
||||
typeof e.window === "number") &&
|
||||
(typeof e.gapAllowance === "undefined" ||
|
||||
typeof e.gapAllowance === "number") &&
|
||||
(typeof e.usePostAsReference === "undefined" ||
|
||||
e.usePostAsReference === false ||
|
||||
e.usePostAsReference === true) &&
|
||||
(typeof e.include === "undefined" ||
|
||||
Array.isArray(e.include) &&
|
||||
e.include.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.exclude === "undefined" ||
|
||||
Array.isArray(e.exclude) &&
|
||||
e.exclude.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
typeof e.name === "string") &&
|
||||
(typeof e.authors === "undefined" ||
|
||||
(e.authors !== null &&
|
||||
typeof e.authors === "object" ||
|
||||
typeof e.authors === "function") &&
|
||||
(typeof e.authors.exclude === "undefined" ||
|
||||
Array.isArray(e.authors.exclude) &&
|
||||
e.authors.exclude.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
)) &&
|
||||
(typeof e.authors.include === "undefined" ||
|
||||
Array.isArray(e.authors.include) &&
|
||||
e.authors.include.every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(typeof e.name === "undefined" ||
|
||||
Array.isArray(e.name) &&
|
||||
e.name.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairCssClass === "undefined" ||
|
||||
Array.isArray(e.flairCssClass) &&
|
||||
e.flairCssClass.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.flairText === "undefined" ||
|
||||
Array.isArray(e.flairText) &&
|
||||
e.flairText.every((e: any) =>
|
||||
typeof e === "string"
|
||||
)) &&
|
||||
(typeof e.isMod === "undefined" ||
|
||||
e.isMod === false ||
|
||||
e.isMod === true)
|
||||
))) &&
|
||||
(e.kind === "recentActivity" ||
|
||||
e.kind === "repeatSubmission" ||
|
||||
e.kind === "author"))
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -8,6 +8,10 @@ import {createLabelledLogger, determineNewResults, findResultByPremise, loggerMe
|
||||
import {Logger} from "winston";
|
||||
import {AuthorRuleJSONConfig} from "./AuthorRule";
|
||||
import {JoinCondition, JoinOperands} from "../Common/interfaces";
|
||||
import * as RuleSchema from '../Schema/Rule.json';
|
||||
import Ajv from 'ajv';
|
||||
|
||||
const ajv = new Ajv();
|
||||
|
||||
export class RuleSet implements IRuleSet, Triggerable {
|
||||
rules: Rule[] = [];
|
||||
@@ -25,10 +29,15 @@ export class RuleSet implements IRuleSet, Triggerable {
|
||||
for (const r of rules) {
|
||||
if (r instanceof Rule) {
|
||||
this.rules.push(r);
|
||||
} else if (isRuleConfig(r)) {
|
||||
// @ts-ignore
|
||||
r.logger = this.logger;
|
||||
this.rules.push(ruleFactory(r));
|
||||
} else {
|
||||
const valid = ajv.validate(RuleSchema, r);
|
||||
if (valid) {
|
||||
// @ts-ignore
|
||||
r.logger = this.logger;
|
||||
this.rules.push(ruleFactory(r as RuleJSONConfig));
|
||||
} else {
|
||||
this.logger.warn('Could not build rule because of JSON errors', {}, {errors: ajv.errors, obj: r});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=index.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.guard.js","sourceRoot":"","sources":["index.guard.ts"],"names":[],"mappings":""}
|
||||
29
src/Schema/Action.json
Normal file
29
src/Schema/Action.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"description": "The type of action that will be performed",
|
||||
"enum": [
|
||||
"comment",
|
||||
"flair",
|
||||
"lock",
|
||||
"remove",
|
||||
"report"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "A friendly name for this Action",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"kind",
|
||||
"name"
|
||||
],
|
||||
"required": [
|
||||
"kind"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
@@ -406,8 +406,7 @@
|
||||
"properties": {
|
||||
"interval": {
|
||||
"default": 10000,
|
||||
"description": "Amount of time to wait between requests for new comments\n\nDefaults to 10 seconds",
|
||||
"format": "milliseconds",
|
||||
"description": "Amount of time, in milliseconds, to wait between requests for new comments",
|
||||
"type": "number"
|
||||
},
|
||||
"limit": {
|
||||
@@ -427,8 +426,7 @@
|
||||
"properties": {
|
||||
"interval": {
|
||||
"default": 10000,
|
||||
"description": "Amount of time to wait between requests to /r/subreddit/new\n\nDefaults to 10 seconds",
|
||||
"format": "milliseconds",
|
||||
"description": "Amount of time, in milliseconds, to wait between requests to /r/subreddit/new",
|
||||
"type": "number"
|
||||
},
|
||||
"limit": {
|
||||
105
src/Schema/Rule.json
Normal file
105
src/Schema/Rule.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AuthorCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "Criteria with which to test against the author of an Activity. The outcome of the test is based on:\n\n1. All present properties passing and\n2. If a property is a list then any value from the list matching",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"flairCssClass": {
|
||||
"description": "A list of (user) flair css class values from the subreddit to match against",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"flairText": {
|
||||
"description": "A list of (user) flair text values from the subreddit to match against",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"isMod": {
|
||||
"description": "Is the author a moderator?",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "A list of reddit usernames (case-insensitive) to match against. Do not include the \"u/\" prefix\n\n EX to match against /u/FoxxMD and /u/AnotherUser use [\"FoxxMD\",\"AnotherUser\"]",
|
||||
"examples": [
|
||||
"FoxxMD",
|
||||
"AnotherUser"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"name",
|
||||
"flairCssClass",
|
||||
"flairText",
|
||||
"isMod"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AuthorOptions": {
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"exclude": {
|
||||
"description": "Only runs if include is not present. Will \"pass\" if any of set of the AuthorCriteria does not pass",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"include": {
|
||||
"description": "Will \"pass\" if any set of AuthorCriteria passes",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"include",
|
||||
"exclude"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"authors": {
|
||||
"$ref": "#/definitions/AuthorOptions",
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1
|
||||
},
|
||||
"kind": {
|
||||
"description": "The kind of rule to run",
|
||||
"enum": [
|
||||
"author",
|
||||
"recentActivity",
|
||||
"repeatSubmission"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "A friendly, descriptive name for this rule. Highly recommended to make it easier to track logs EX \"repeatCrosspostRule\"",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"kind",
|
||||
"name",
|
||||
"authors"
|
||||
],
|
||||
"required": [
|
||||
"kind"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
465
src/Schema/RuleSet.json
Normal file
465
src/Schema/RuleSet.json
Normal file
@@ -0,0 +1,465 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"ActivityWindowCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "If both properties are defined then the first criteria met will be used IE if # of activities = count before duration is reached then count will be used, or vice versa",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "The number of activities (submission/comments) to consider",
|
||||
"type": "number"
|
||||
},
|
||||
"duration": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DurationObject"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"description": "An ISO 8601 duration or Day.js duration object.\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"examples": [
|
||||
"PT1M",
|
||||
{
|
||||
"minutes": 15
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"count",
|
||||
"duration"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AuthorCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "Criteria with which to test against the author of an Activity. The outcome of the test is based on:\n\n1. All present properties passing and\n2. If a property is a list then any value from the list matching",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"flairCssClass": {
|
||||
"description": "A list of (user) flair css class values from the subreddit to match against",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"flairText": {
|
||||
"description": "A list of (user) flair text values from the subreddit to match against",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"isMod": {
|
||||
"description": "Is the author a moderator?",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "A list of reddit usernames (case-insensitive) to match against. Do not include the \"u/\" prefix\n\n EX to match against /u/FoxxMD and /u/AnotherUser use [\"FoxxMD\",\"AnotherUser\"]",
|
||||
"examples": [
|
||||
"FoxxMD",
|
||||
"AnotherUser"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"name",
|
||||
"flairCssClass",
|
||||
"flairText",
|
||||
"isMod"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AuthorOptions": {
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"exclude": {
|
||||
"description": "Only runs if include is not present. Will \"pass\" if any of set of the AuthorCriteria does not pass",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"include": {
|
||||
"description": "Will \"pass\" if any set of AuthorCriteria passes",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"include",
|
||||
"exclude"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AuthorRuleJSONConfig": {
|
||||
"properties": {
|
||||
"authors": {
|
||||
"$ref": "#/definitions/AuthorOptions",
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1
|
||||
},
|
||||
"exclude": {
|
||||
"description": "Only runs if include is not present. Will \"pass\" if any of set of the AuthorCriteria does not pass",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"include": {
|
||||
"description": "Will \"pass\" if any set of AuthorCriteria passes",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuthorCriteria"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"kind": {
|
||||
"description": "The kind of rule to run",
|
||||
"enum": [
|
||||
"author"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "A friendly, descriptive name for this rule. Highly recommended to make it easier to track logs EX \"repeatCrosspostRule\"",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"kind",
|
||||
"include",
|
||||
"exclude",
|
||||
"name",
|
||||
"authors"
|
||||
],
|
||||
"required": [
|
||||
"exclude",
|
||||
"include",
|
||||
"kind"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DurationObject": {
|
||||
"additionalProperties": false,
|
||||
"description": "A Day.js duration object\n\nhttps://day.js.org/docs/en/durations/creating",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"days": {
|
||||
"type": "number"
|
||||
},
|
||||
"hours": {
|
||||
"type": "number"
|
||||
},
|
||||
"minutes": {
|
||||
"type": "number"
|
||||
},
|
||||
"months": {
|
||||
"type": "number"
|
||||
},
|
||||
"seconds": {
|
||||
"type": "number"
|
||||
},
|
||||
"weeks": {
|
||||
"type": "number"
|
||||
},
|
||||
"years": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"seconds",
|
||||
"minutes",
|
||||
"hours",
|
||||
"days",
|
||||
"weeks",
|
||||
"months",
|
||||
"years"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RecentActivityRuleJSONConfig": {
|
||||
"description": "Checks a user's history for any Activity (Submission/Comment) in the subreddits specified in thresholds",
|
||||
"properties": {
|
||||
"authors": {
|
||||
"$ref": "#/definitions/AuthorOptions",
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1
|
||||
},
|
||||
"kind": {
|
||||
"description": "The kind of rule to run",
|
||||
"enum": [
|
||||
"recentActivity"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"lookAt": {
|
||||
"description": "If present restricts the activities that are considered for count from SubThreshold",
|
||||
"enum": [
|
||||
"comments",
|
||||
"submissions"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "A friendly, descriptive name for this rule. Highly recommended to make it easier to track logs EX \"repeatCrosspostRule\"",
|
||||
"type": "string"
|
||||
},
|
||||
"thresholds": {
|
||||
"description": "A list of subreddits/count criteria that may trigger this rule. ANY SubThreshold will trigger this rule.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/SubThreshold"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"useSubmissionAsReference": {
|
||||
"default": true,
|
||||
"description": "If activity is a Submission and is a link (not self-post) then only look at Submissions that contain this link, otherwise consider all activities.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"window": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DurationObject"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ActivityWindowCriteria"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
]
|
||||
}
|
||||
],
|
||||
"default": 15,
|
||||
"description": "Criteria for defining what set of activities should be considered.\n\nThe value of this property may be either count OR duration -- to use both write it as an ActivityWindowCriteria\n\nSee ActivityWindowCriteria for descriptions of what count/duration do",
|
||||
"examples": [
|
||||
15,
|
||||
"PT1M",
|
||||
{
|
||||
"count": 10
|
||||
},
|
||||
{
|
||||
"duration": {
|
||||
"hours": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": 5,
|
||||
"duration": {
|
||||
"minutes": 15
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"kind",
|
||||
"lookAt",
|
||||
"thresholds",
|
||||
"window",
|
||||
"useSubmissionAsReference",
|
||||
"name",
|
||||
"authors"
|
||||
],
|
||||
"required": [
|
||||
"kind",
|
||||
"thresholds"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RepeatSubmissionJSONConfig": {
|
||||
"description": "Checks a user's history for Submissions with identical content",
|
||||
"properties": {
|
||||
"authors": {
|
||||
"$ref": "#/definitions/AuthorOptions",
|
||||
"additionalProperties": false,
|
||||
"description": "If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.",
|
||||
"minProperties": 1
|
||||
},
|
||||
"exclude": {
|
||||
"description": "Do not include Submissions from this list of Subreddits.\n\nA list of subreddits (case-insensitive) to look for. Do not include \"r/\" prefix.\n\nEX to match against /r/mealtimevideos and /r/askscience use [\"mealtimevideos\",\"askscience\"]",
|
||||
"examples": [
|
||||
"mealtimevideos",
|
||||
"askscience"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"gapAllowance": {
|
||||
"description": "The number of allowed non-identical Submissions between identical Submissions that can be ignored when checking against the threshold value",
|
||||
"type": "number"
|
||||
},
|
||||
"include": {
|
||||
"description": "Only include Submissions from this list of Subreddits.\n\nA list of subreddits (case-insensitive) to look for. Do not include \"r/\" prefix.\n\nEX to match against /r/mealtimevideos and /r/askscience use [\"mealtimevideos\",\"askscience\"]",
|
||||
"examples": [
|
||||
"mealtimevideos",
|
||||
"askscience"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"kind": {
|
||||
"description": "The kind of rule to run",
|
||||
"enum": [
|
||||
"repeatSubmission"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "A friendly, descriptive name for this rule. Highly recommended to make it easier to track logs EX \"repeatCrosspostRule\"",
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": 5,
|
||||
"description": "The number of repeat submissions that will trigger the rule",
|
||||
"type": "number"
|
||||
},
|
||||
"useSubmissionAsReference": {
|
||||
"default": true,
|
||||
"description": "If activity is a Submission and is a link (not self-post) then only look at Submissions that contain this link, otherwise consider all activities.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"window": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DurationObject"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ActivityWindowCriteria"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
]
|
||||
}
|
||||
],
|
||||
"default": 15,
|
||||
"description": "Criteria for defining what set of activities should be considered.\n\nThe value of this property may be either count OR duration -- to use both write it as an ActivityWindowCriteria\n\nSee ActivityWindowCriteria for descriptions of what count/duration do",
|
||||
"examples": [
|
||||
15,
|
||||
"PT1M",
|
||||
{
|
||||
"count": 10
|
||||
},
|
||||
{
|
||||
"duration": {
|
||||
"hours": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": 5,
|
||||
"duration": {
|
||||
"minutes": 15
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"kind",
|
||||
"threshold",
|
||||
"gapAllowance",
|
||||
"include",
|
||||
"exclude",
|
||||
"window",
|
||||
"useSubmissionAsReference",
|
||||
"name",
|
||||
"authors"
|
||||
],
|
||||
"required": [
|
||||
"kind"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SubThreshold": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "The number of activities in each subreddit from the list that will trigger this rule",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
},
|
||||
"subreddits": {
|
||||
"description": "A list of subreddits (case-insensitive) to look for. Do not include \"r/\" prefix.\n\nEX to match against /r/mealtimevideos and /r/askscience use [\"mealtimevideos\",\"askscience\"]",
|
||||
"examples": [
|
||||
"mealtimevideos",
|
||||
"askscience"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"count",
|
||||
"subreddits"
|
||||
],
|
||||
"required": [
|
||||
"subreddits"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "A RuleSet is a \"nested\" set of Rules that can be used to create more complex AND/OR behavior. Think of the outcome of a RuleSet as the result of all of it's Rules (based on condition)",
|
||||
"properties": {
|
||||
"condition": {
|
||||
"default": "AND",
|
||||
"description": "Under what condition should a set of rules be considered \"successful\"?\n\nIf \"OR\" then ANY triggered rule results in success.\n\nIf \"AND\" then ALL rules must be triggered to result in success.",
|
||||
"enum": [
|
||||
"AND",
|
||||
"OR"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"rules": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/RecentActivityRuleJSONConfig"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/RepeatSubmissionJSONConfig"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/AuthorRuleJSONConfig"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"propertyOrder": [
|
||||
"rules",
|
||||
"condition"
|
||||
],
|
||||
"required": [
|
||||
"rules"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import {InboxStream, CommentStream, SubmissionStream} from "snoostorm";
|
||||
import snoowrap from "snoowrap";
|
||||
import minimist from 'minimist';
|
||||
import winston, {Logger} from 'winston';
|
||||
import winston from 'winston';
|
||||
import 'winston-daily-rotate-file';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc.js';
|
||||
import dduration from 'dayjs/plugin/duration.js';
|
||||
import {labelledFormat} from "./util";
|
||||
import {ConfigBuilder} from "./ConfigBuilder";
|
||||
import EventEmitter from "events";
|
||||
import {Manager} from "./Subreddit/Manager";
|
||||
import pEvent from "p-event";
|
||||
import {JSONConfig} from "./JsonConfig";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(dduration);
|
||||
@@ -119,7 +116,6 @@ if (subredditsArg.length === 0) {
|
||||
for (const sub of subsToRun) {
|
||||
let content = undefined;
|
||||
let json = undefined;
|
||||
let config = undefined;
|
||||
try {
|
||||
const wiki = sub.getWikiPage('contextbot');
|
||||
content = await wiki.content_md;
|
||||
|
||||
Reference in New Issue
Block a user