mirror of
https://github.com/FoxxMD/context-mod.git
synced 2026-04-19 03:00:07 -04:00
Refactor most thresholds to use comparison operators (like automod)
This commit is contained in:
@@ -404,7 +404,7 @@ export interface ManagerOptions {
|
||||
/**
|
||||
* A string containing a comparison operator and a value to compare against
|
||||
*
|
||||
* The syntax is `< OR > OR <= OR >=] [number][?percent sign]`
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign]`
|
||||
*
|
||||
* * EX `> 100` => greater than 100
|
||||
* * EX `<= 75%` => less than or equal to 75%
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Rule, RuleJSONConfig, RuleOptions, RuleResult} from "./index";
|
||||
import Submission from "snoowrap/dist/objects/Submission";
|
||||
import {getAuthorActivities} from "../Utils/SnoowrapUtils";
|
||||
import dayjs from "dayjs";
|
||||
import {comparisonTextOp, formatNumber, parseGenericComparison, percentFromString} from "../util";
|
||||
import {comparisonTextOp, formatNumber, parseGenericValueOrPercentComparison, percentFromString} from "../util";
|
||||
|
||||
export interface CommentThresholdCriteria extends ThresholdCriteria {
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ export interface HistoryCriteria {
|
||||
/**
|
||||
* A string containing a comparison operator and a value to compare submissions against
|
||||
*
|
||||
* The syntax is `[< OR > OR <= OR >=] [number][?percent sign]`
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign]`
|
||||
*
|
||||
* * EX `> 100` => greater than 100 submissions
|
||||
* * EX `<= 75%` => submissions are equal to or less than 75% of all Activities
|
||||
@@ -34,12 +34,12 @@ export interface HistoryCriteria {
|
||||
/**
|
||||
* A string containing a comparison operator and a value to compare comments against
|
||||
*
|
||||
* The syntax is `[< OR > OR <= OR >=] [number][?percent sign]`
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign] [OP]`
|
||||
*
|
||||
* * EX `> 100` => greater than 100 comments
|
||||
* * EX `<= 75%` => comments are equal to or less than 75% of all Activities
|
||||
*
|
||||
* If your string also contains the text `OP` somewhere **after** `[number][?percent sign]`...:
|
||||
* If your string also contains the text `OP` somewhere **after** `<number>[percent sign]`...:
|
||||
*
|
||||
* * EX `> 100 OP` => greater than 100 comments as OP
|
||||
* * EX `<= 25% as OP` => Comments as OP were less then or equal to 25% of **all Comments**
|
||||
@@ -131,7 +131,7 @@ export class HistoryRule extends Rule {
|
||||
|
||||
let commentTrigger = undefined;
|
||||
if(comment !== undefined) {
|
||||
const {operator, value, isPercent, extra = ''} = parseGenericComparison(comment);
|
||||
const {operator, value, isPercent, extra = ''} = parseGenericValueOrPercentComparison(comment);
|
||||
const asOp = extra.toLowerCase().includes('op');
|
||||
if(isPercent) {
|
||||
const per = value / 100;
|
||||
@@ -151,7 +151,7 @@ export class HistoryRule extends Rule {
|
||||
|
||||
let submissionTrigger = undefined;
|
||||
if(submission !== undefined) {
|
||||
const {operator, value, isPercent} = parseGenericComparison(submission);
|
||||
const {operator, value, isPercent} = parseGenericValueOrPercentComparison(submission);
|
||||
if(isPercent) {
|
||||
const per = value / 100;
|
||||
submissionTrigger = comparisonTextOp(submissionTotal / activityTotal, operator, per);
|
||||
@@ -218,14 +218,14 @@ export class HistoryRule extends Rule {
|
||||
let submissionSummary;
|
||||
let commentSummary;
|
||||
if(submission !== undefined) {
|
||||
const {operator, value, isPercent, displayText} = parseGenericComparison(submission);
|
||||
const {operator, value, isPercent, displayText} = parseGenericValueOrPercentComparison(submission);
|
||||
const suffix = !isPercent ? 'Items' : `(${formatNumber((submissionTotal/activityTotal)*100)}%) of ${activityTotal} Total`;
|
||||
submissionSummary = `Submissions (${submissionTotal}) were ${displayText} ${suffix}`;
|
||||
data.submissionSummary = submissionSummary;
|
||||
thresholdSummary.push(submissionSummary);
|
||||
}
|
||||
if(comment !== undefined) {
|
||||
const {operator, value, isPercent, displayText, extra = ''} = parseGenericComparison(comment);
|
||||
const {operator, value, isPercent, displayText, extra = ''} = parseGenericValueOrPercentComparison(comment);
|
||||
const asOp = extra.toLowerCase().includes('op');
|
||||
const totalType = asOp ? 'Comments' : 'Activities'
|
||||
const countType = asOp ? 'Comments as OP' : 'Comments';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Rule, RuleJSONConfig, RuleOptions, RulePremise, RuleResult} from "./index";
|
||||
import {Comment, VoteableContent} from "snoowrap";
|
||||
import Submission from "snoowrap/dist/objects/Submission";
|
||||
import {activityWindowText, parseUsableLinkIdentifier} from "../util";
|
||||
import {activityWindowText, comparisonTextOp, parseGenericValueOrPercentComparison, parseUsableLinkIdentifier} from "../util";
|
||||
import {
|
||||
ActivityWindow,
|
||||
ActivityWindowCriteria,
|
||||
@@ -59,7 +59,6 @@ export class RecentActivityRule extends Rule {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
let viableActivity = activities;
|
||||
if (this.useSubmissionAsReference) {
|
||||
if (!(item instanceof Submission)) {
|
||||
@@ -84,53 +83,51 @@ export class RecentActivityRule extends Rule {
|
||||
grouped[s] = (grouped[s] || []).concat(activity);
|
||||
return grouped;
|
||||
}, {} as Record<string, (Submission | Comment)[]>);
|
||||
let triggeredPerSub = [];
|
||||
|
||||
|
||||
let totalTriggeredOn;
|
||||
for (const triggerSet of this.thresholds) {
|
||||
triggeredPerSub = [];
|
||||
let currCount = 0;
|
||||
let presentSubs = [];
|
||||
const {count: subCount = 1, totalCount = 1, subreddits = []} = triggerSet;
|
||||
let presentSubs;
|
||||
const {threshold = '>= 1', subreddits = []} = triggerSet;
|
||||
for (const sub of subreddits) {
|
||||
const isub = sub.toLowerCase();
|
||||
const {[isub]: tSub = []} = groupedActivity;
|
||||
if(tSub.length > 0) {
|
||||
if (tSub.length > 0) {
|
||||
currCount += tSub.length;
|
||||
presentSubs.push(sub);
|
||||
if (subCount !== undefined && tSub.length >= subCount) {
|
||||
triggeredPerSub.push({subreddit: sub, count: tSub.length, threshold: subCount});
|
||||
if(presentSubs === undefined) {
|
||||
presentSubs = [];
|
||||
}
|
||||
presentSubs.push(sub);
|
||||
}
|
||||
}
|
||||
if(totalCount !== undefined && currCount >= totalCount) {
|
||||
totalTriggeredOn = {subreddits: presentSubs, count: currCount, threshold: totalCount};
|
||||
const {operator, value, isPercent} = parseGenericValueOrPercentComparison(threshold);
|
||||
if (threshold !== undefined) {
|
||||
if (isPercent) {
|
||||
if (comparisonTextOp(currCount / viableActivity.length, operator, value / 100)) {
|
||||
totalTriggeredOn = {subreddits: presentSubs || subreddits, count: currCount, threshold};
|
||||
}
|
||||
} else if (comparisonTextOp(currCount, operator, value)) {
|
||||
totalTriggeredOn = {subreddits: presentSubs || subreddits, count: currCount, threshold};
|
||||
}
|
||||
}
|
||||
// if either trigger condition is hit end the iteration early
|
||||
if(triggeredPerSub.length > 0 || totalTriggeredOn !== undefined) {
|
||||
if (totalTriggeredOn !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (triggeredPerSub.length > 0 || totalTriggeredOn !== undefined) {
|
||||
if (totalTriggeredOn !== undefined) {
|
||||
let resultArr = [];
|
||||
const data: any = {};
|
||||
if(triggeredPerSub.length > 0) {
|
||||
data.perSubCount = triggeredPerSub.length;
|
||||
data.perSubTotal = triggeredPerSub.reduce((acc, x) => acc + x.count, 0);
|
||||
data.perSubSubredditsSummary = triggeredPerSub.map(x => x.subreddit).join(', ');
|
||||
data.perSubSummary = triggeredPerSub.map(x => `${x.subreddit}(${x.count})`).join(', ');
|
||||
data.perSubThreshold = triggeredPerSub[0].threshold;
|
||||
resultArr.push(`${triggeredPerSub.length} subs have >${triggeredPerSub[0].threshold} activities (${data.perSubTotal} Total)`);
|
||||
}
|
||||
if(totalTriggeredOn !== undefined) {
|
||||
data.totalCount = totalTriggeredOn.count;
|
||||
data.totalSubredditsCount = totalTriggeredOn.subreddits.length;
|
||||
data.totalSubredditsSummary = totalTriggeredOn.subreddits.join(', ')
|
||||
data.totalThreshold = totalTriggeredOn.threshold;
|
||||
data.totalSummary = `${data.totalCount} (>${totalTriggeredOn.threshold}) activities over ${totalTriggeredOn.subreddits.length} subreddits`;
|
||||
resultArr.push(data.totalSummary);
|
||||
}
|
||||
data.totalCount = totalTriggeredOn.count;
|
||||
data.totalSubredditsCount = totalTriggeredOn.subreddits.length;
|
||||
data.totalSubredditsSummary = totalTriggeredOn.subreddits.join(', ')
|
||||
data.totalThreshold = totalTriggeredOn.threshold;
|
||||
data.totalSummary = `${data.totalCount} (${totalTriggeredOn.threshold}) activities over ${totalTriggeredOn.subreddits.length} subreddits`;
|
||||
resultArr.push(data.totalSummary);
|
||||
|
||||
let summary;
|
||||
if(resultArr.length === 2) {
|
||||
if (resultArr.length === 2) {
|
||||
// need a shortened summary
|
||||
summary = `${data.perSubCount} per-sub triggers (${data.perSubThreshold}) and ${data.totalCount} total (${data.totalThreshold})`
|
||||
} else {
|
||||
@@ -142,11 +139,11 @@ export class RecentActivityRule extends Rule {
|
||||
result,
|
||||
data: {
|
||||
window: typeof this.window === 'number' ? `${activities.length} Items` : activityWindowText(viableActivity),
|
||||
triggeredOn: triggeredPerSub,
|
||||
//triggeredOn: triggeredPerSub,
|
||||
summary,
|
||||
subSummary: data.totalSubredditsSummary|| data.perSubSubredditsSummary,
|
||||
subCount: data.totalSubredditsCount || data.perSubCount,
|
||||
totalCount: data.totalCount || data.perSubTotal
|
||||
subSummary: data.totalSubredditsSummary,
|
||||
subCount: data.totalSubredditsCount,
|
||||
totalCount: data.totalCount
|
||||
}
|
||||
})]]);
|
||||
}
|
||||
@@ -163,19 +160,20 @@ export class RecentActivityRule extends Rule {
|
||||
* */
|
||||
export interface SubThreshold extends SubredditCriteria {
|
||||
/**
|
||||
* The number of activities in each subreddit from the list that will trigger this rule
|
||||
* @minimum 1
|
||||
* @default 1
|
||||
* @examples [1]
|
||||
* A string containing a comparison operator and a value to compare recent activities against
|
||||
*
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign]`
|
||||
*
|
||||
* * EX `> 3` => greater than 3 activities found in the listed subreddits
|
||||
* * EX `<= 75%` => number of Activities in the subreddits listed are equal to or less than 75% of all Activities
|
||||
*
|
||||
* **Note:** If you use percentage comparison here as well as `useSubmissionAsReference` then "all Activities" is only pertains to Activities that had the Link of the Submission, rather than all Activities from this window.
|
||||
*
|
||||
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(%?)(.*)$
|
||||
* @default ">= 1"
|
||||
* @examples [">= 1"]
|
||||
* */
|
||||
count?: number,
|
||||
/**
|
||||
* The total number of activities across all listed subreddits that will trigger this rule
|
||||
* @minimum 1
|
||||
* @default 1
|
||||
* @examples [1]
|
||||
* */
|
||||
totalCount?: number
|
||||
threshold?: string
|
||||
}
|
||||
|
||||
interface RecentActivityConfig extends ActivityWindow, ReferenceSubmission {
|
||||
|
||||
@@ -4,18 +4,22 @@ import {RuleOptions, RuleResult} from "../index";
|
||||
import Submission from "snoowrap/dist/objects/Submission";
|
||||
import {getAttributionIdentifier} from "../../Utils/SnoowrapUtils";
|
||||
import dayjs from "dayjs";
|
||||
import {comparisonTextOp, parseGenericValueOrPercentComparison} from "../../util";
|
||||
|
||||
|
||||
export interface AttributionCriteria {
|
||||
/**
|
||||
* The number or percentage to trigger this rule at
|
||||
* A string containing a comparison operator and a value to compare comments against
|
||||
*
|
||||
* * If `threshold` is a `number` then it is the absolute number of attribution instances to trigger at
|
||||
* * If `threshold` is a `string` with percentage (EX `40%`) then it is the percentage of the total (see `lookAt`) this attribution must reach to trigger
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign]`
|
||||
*
|
||||
* @default 10%
|
||||
* * EX `> 12` => greater than 12 activities originate from same attribution
|
||||
* * EX `<= 10%` => less than 10% of all Activities have the same attribution
|
||||
*
|
||||
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(%?)(.*)$
|
||||
* @default "> 10%"
|
||||
* */
|
||||
threshold: number | string
|
||||
threshold: string
|
||||
window: ActivityWindowType
|
||||
/**
|
||||
* What activities to use for total count when determining what percentage an attribution comprises
|
||||
@@ -107,12 +111,8 @@ export class AttributionRule extends SubmissionRule {
|
||||
|
||||
for (const criteria of this.criteria) {
|
||||
|
||||
const {threshold, window, thresholdOn = 'all', minActivityCount = 5} = criteria;
|
||||
|
||||
let percentVal;
|
||||
if (typeof threshold === 'string') {
|
||||
percentVal = Number.parseInt(threshold.replace('%', '')) / 100;
|
||||
}
|
||||
const {threshold = '> 10%', window, thresholdOn = 'all', minActivityCount = 10} = criteria;
|
||||
const {operator, value, isPercent, extra = ''} = parseGenericValueOrPercentComparison(threshold);
|
||||
|
||||
let activities = thresholdOn === 'submissions' ? await this.resources.getAuthorSubmissions(item.author, {window: window}) : await this.resources.getAuthorActivities(item.author, {window: window});
|
||||
activities = activities.filter(act => {
|
||||
@@ -172,11 +172,11 @@ export class AttributionRule extends SubmissionRule {
|
||||
let triggeredDomains = [];
|
||||
for (const [domain, subCount] of aggregatedSubmissions) {
|
||||
let triggered = false;
|
||||
if (percentVal !== undefined) {
|
||||
|
||||
triggered = percentVal <= subCount / activityTotal;
|
||||
} else if (subCount >= threshold) {
|
||||
triggered = true;
|
||||
if(isPercent) {
|
||||
triggered = comparisonTextOp(subCount / activityTotal, operator, (value/100));
|
||||
}
|
||||
else {
|
||||
triggered = comparisonTextOp(subCount, operator, value);
|
||||
}
|
||||
|
||||
if (triggered) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import {SubmissionRule, SubmissionRuleJSONConfig} from "./index";
|
||||
import {RuleOptions, RuleResult} from "../index";
|
||||
import {Comment} from "snoowrap";
|
||||
import {activityWindowText, parseUsableLinkIdentifier as linkParser} from "../../util";
|
||||
import {
|
||||
activityWindowText,
|
||||
comparisonTextOp,
|
||||
parseGenericValueComparison,
|
||||
parseUsableLinkIdentifier as linkParser
|
||||
} from "../../util";
|
||||
import {ActivityWindow, ActivityWindowType, ReferenceSubmission} from "../../Common/interfaces";
|
||||
import Submission from "snoowrap/dist/objects/Submission";
|
||||
import dayjs from "dayjs";
|
||||
@@ -33,7 +38,7 @@ const getActivityIdentifier = (activity: (Submission | Comment), length = 200) =
|
||||
}
|
||||
|
||||
export class RepeatActivityRule extends SubmissionRule {
|
||||
threshold: number;
|
||||
threshold: string;
|
||||
window: ActivityWindowType;
|
||||
gapAllowance?: number;
|
||||
useSubmissionAsReference: boolean;
|
||||
@@ -44,7 +49,7 @@ export class RepeatActivityRule extends SubmissionRule {
|
||||
constructor(options: RepeatActivityOptions) {
|
||||
super(options);
|
||||
const {
|
||||
threshold = 5,
|
||||
threshold = '> 5',
|
||||
window = 100,
|
||||
gapAllowance,
|
||||
useSubmissionAsReference = true,
|
||||
@@ -139,6 +144,10 @@ export class RepeatActivityRule extends SubmissionRule {
|
||||
applicableGroupedActivities.set(getActivityIdentifier(item), referenceSubmissions || [])
|
||||
}
|
||||
|
||||
const {operator, value: thresholdValue} = parseGenericValueComparison(this.threshold);
|
||||
const greaterThan = operator.includes('>');
|
||||
let allLessThan = true;
|
||||
|
||||
const identifiersSummary: SummaryData[] = [];
|
||||
for (let [key, value] of applicableGroupedActivities) {
|
||||
const summaryData = {
|
||||
@@ -150,23 +159,39 @@ export class RepeatActivityRule extends SubmissionRule {
|
||||
triggeringSetsMarkdown: [],
|
||||
};
|
||||
for (let set of value) {
|
||||
if (set.length >= this.threshold) {
|
||||
// @ts-ignore
|
||||
summaryData.triggeringSets.push(set);
|
||||
summaryData.totalTriggeringSets++;
|
||||
summaryData.largestTrigger = Math.max(summaryData.largestTrigger, set.length);
|
||||
const md = set.map((x: (Comment | Submission)) => `[${x instanceof Submission ? x.title : getActivityIdentifier(x, 50)}](https://reddit.com${x.permalink}) in ${x.subreddit_name_prefixed} on ${dayjs(x.created_utc * 1000).utc().format()}`);
|
||||
// @ts-ignore
|
||||
summaryData.triggeringSetsMarkdown.push(md);
|
||||
const test = comparisonTextOp(set.length, operator, thresholdValue);
|
||||
if(test) {
|
||||
// if(greaterThan) {
|
||||
// @ts-ignore
|
||||
summaryData.triggeringSets.push(set);
|
||||
summaryData.totalTriggeringSets++;
|
||||
summaryData.largestTrigger = Math.max(summaryData.largestTrigger, set.length);
|
||||
const md = set.map((x: (Comment | Submission)) => `[${x instanceof Submission ? x.title : getActivityIdentifier(x, 50)}](https://reddit.com${x.permalink}) in ${x.subreddit_name_prefixed} on ${dayjs(x.created_utc * 1000).utc().format()}`);
|
||||
// @ts-ignore
|
||||
summaryData.triggeringSetsMarkdown.push(md);
|
||||
// }
|
||||
} else if(!greaterThan) {
|
||||
allLessThan = false;
|
||||
}
|
||||
// if ((test && greaterThan) || (!test && !greaterThan)) {
|
||||
// // @ts-ignore
|
||||
// summaryData.triggeringSets.push(set);
|
||||
// summaryData.totalTriggeringSets++;
|
||||
// summaryData.largestTrigger = Math.max(summaryData.largestTrigger, set.length);
|
||||
// const md = set.map((x: (Comment | Submission)) => `[${x instanceof Submission ? x.title : getActivityIdentifier(x, 50)}](https://reddit.com${x.permalink}) in ${x.subreddit_name_prefixed} on ${dayjs(x.created_utc * 1000).utc().format()}`);
|
||||
// // @ts-ignore
|
||||
// summaryData.triggeringSetsMarkdown.push(md);
|
||||
// }
|
||||
}
|
||||
if(greaterThan || (!greaterThan && allLessThan)) {
|
||||
identifiersSummary.push(summaryData);
|
||||
}
|
||||
identifiersSummary.push(summaryData);
|
||||
}
|
||||
|
||||
const triggeringSummaries = identifiersSummary.filter(x => x.totalTriggeringSets > 0)
|
||||
if (triggeringSummaries.length > 0) {
|
||||
const largestRepeat = triggeringSummaries.reduce((acc, summ) => Math.max(summ.largestTrigger, acc), 0);
|
||||
const result = `${triggeringSummaries.length} of ${identifiersSummary.length} unique items repeated >=${this.threshold} (threshold) times, largest repeat: ${largestRepeat}`;
|
||||
const result = `${triggeringSummaries.length} of ${identifiersSummary.length} unique items repeated ${this.threshold} (threshold) times, largest repeat: ${largestRepeat}`;
|
||||
this.logger.verbose(result);
|
||||
return Promise.resolve([true, [this.getResult(true, {
|
||||
result,
|
||||
@@ -198,9 +223,9 @@ interface SummaryData {
|
||||
interface RepeatActivityConfig extends ActivityWindow, ReferenceSubmission {
|
||||
/**
|
||||
* The number of repeat submissions that will trigger the rule
|
||||
* @default 5
|
||||
* @default ">= 5"
|
||||
* */
|
||||
threshold?: number,
|
||||
threshold?: string,
|
||||
/**
|
||||
* The number of allowed non-identical Submissions between identical Submissions that can be ignored when checking against the threshold value
|
||||
* */
|
||||
|
||||
@@ -149,31 +149,51 @@ export class Author implements AuthorCriteria {
|
||||
|
||||
export interface UserNoteCriteria {
|
||||
/**
|
||||
* User Note type key
|
||||
* User Note type key to search for
|
||||
* @examples ["spamwarn"]
|
||||
* */
|
||||
type: string;
|
||||
/**
|
||||
* Number of occurrences of this type. Ignored if `search` is `current`
|
||||
* @examples [1]
|
||||
* @default 1
|
||||
*
|
||||
* A string containing a comparison operator and/or a value to compare number of occurrences against
|
||||
*
|
||||
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign] [ascending|descending]`
|
||||
*
|
||||
* @examples [">= 1"]
|
||||
* @default ">= 1"
|
||||
* @pattern ^\s*(?<opStr>>|>=|<|<=)\s*(?<value>\d+)\s*(?<percent>%?)\s*(?<extra>asc.*|desc.*)*$
|
||||
* */
|
||||
count?: number;
|
||||
count?: string;
|
||||
|
||||
/**
|
||||
* * If `current` then only the most recent note is checked
|
||||
* * If `consecutive` then `count` number of `type` notes must be found in a row, based on `order` direction
|
||||
* * If `total` then `count` number of `type` must be found within all notes
|
||||
* How to test the notes for this Author:
|
||||
*
|
||||
* ### current
|
||||
*
|
||||
* Only the most recent note is checked for `type`
|
||||
*
|
||||
* ### total
|
||||
*
|
||||
* The `count` comparison of `type` must be found within all notes
|
||||
*
|
||||
* * EX `count: > 3` => Must have more than 3 notes of `type`, total
|
||||
* * EX `count: <= 25%` => Must have 25% or less of notes of `type`, total
|
||||
*
|
||||
* ### consecutive
|
||||
*
|
||||
* The `count` **number** of `type` notes must be found in a row.
|
||||
*
|
||||
* You may also specify the time-based order in which to search the notes by specifying `ascending (asc)` or `descending (desc)` in the `count` value. Default is `descending`
|
||||
*
|
||||
* * EX `count: >= 3` => Must have 3 or more notes of `type` consecutively, in descending order
|
||||
* * EX `count: < 2` => Must have less than 2 notes of `type` consecutively, in descending order
|
||||
* * EX `count: > 4 asc` => Must have greater than 4 notes of `type` consecutively, in ascending order
|
||||
*
|
||||
* @examples ["current"]
|
||||
* @default current
|
||||
* */
|
||||
search?: 'current' | 'consecutive' | 'total'
|
||||
/**
|
||||
* Time-based order to search Notes in for `consecutive` search
|
||||
* @examples ["descending"]
|
||||
* @default descending
|
||||
* */
|
||||
order?: 'ascending' | 'descending'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,7 +251,7 @@ export interface AuthorCriteria {
|
||||
/**
|
||||
* Test the age of the Author's account (when it was created) against this comparison
|
||||
*
|
||||
* The syntax is `[< OR > OR <= OR >=] [number] [unit]`
|
||||
* The syntax is `(< OR > OR <= OR >=) <number> <unit>`
|
||||
*
|
||||
* * EX `> 100 days` => Passes if Author's account is older than 100 days
|
||||
* * EX `<= 2 months` => Passes if Author's account is younger than or equal to 2 months
|
||||
@@ -248,7 +268,7 @@ export interface AuthorCriteria {
|
||||
/**
|
||||
* A duration and how to compare it against a value
|
||||
*
|
||||
* The syntax is `[< OR > OR <= OR >=] [number] [unit]` EX `> 100 days`, `<= 2 months`
|
||||
* The syntax is `(< OR > OR <= OR >=) <number> <unit>` EX `> 100 days`, `<= 2 months`
|
||||
*
|
||||
* * EX `> 100 days` => Passes if the date being compared is before 100 days ago
|
||||
* * EX `<= 2 months` => Passes if the date being compared is after or equal to 2 months
|
||||
|
||||
@@ -98,12 +98,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": "10%",
|
||||
"description": "The number or percentage to trigger this rule at\n\n* If `threshold` is a `number` then it is the absolute number of attribution instances to trigger at\n* If `threshold` is a `string` with percentage (EX `40%`) then it is the percentage of the total (see `lookAt`) this attribution must reach to trigger",
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
]
|
||||
"default": "> 10%",
|
||||
"description": "A string containing a comparison operator and a value to compare comments against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 12` => greater than 12 activities originate from same attribution\n* EX `<= 10%` => less than 10% of all Activities have the same attribution",
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
},
|
||||
"thresholdOn": {
|
||||
"default": "all",
|
||||
@@ -1361,9 +1359,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": 5,
|
||||
"default": ">= 5",
|
||||
"description": "The number of repeat submissions that will trigger the rule",
|
||||
"type": "number"
|
||||
"type": "string"
|
||||
},
|
||||
"useSubmissionAsReference": {
|
||||
"default": true,
|
||||
@@ -1491,15 +1489,6 @@
|
||||
"description": "At least one count property must be present. If both are present then either can trigger the rule",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "The number of activities in each subreddit from the list that will trigger this rule",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"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": [
|
||||
@@ -1514,14 +1503,14 @@
|
||||
"minItems": 2,
|
||||
"type": "array"
|
||||
},
|
||||
"totalCount": {
|
||||
"default": 1,
|
||||
"description": "The total number of activities across all listed subreddits that will trigger this rule",
|
||||
"threshold": {
|
||||
"default": ">= 1",
|
||||
"description": "A string containing a comparison operator and a value to compare recent activities against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 3` => greater than 3 activities found in the listed subreddits\n* EX `<= 75%` => number of Activities in the subreddits listed are equal to or less than 75% of all Activities\n\n**Note:** If you use percentage comparison here as well as `useSubmissionAsReference` then \"all Activities\" is only pertains to Activities that had the Link of the Submission, rather than all Activities from this window.",
|
||||
"examples": [
|
||||
1
|
||||
">= 1"
|
||||
],
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1816,28 +1805,17 @@
|
||||
"UserNoteCriteria": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`",
|
||||
"default": ">= 1",
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`\n\nA string containing a comparison operator and/or a value to compare number of occurrences against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign] [ascending|descending]`",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"type": "number"
|
||||
},
|
||||
"order": {
|
||||
"default": "descending",
|
||||
"description": "Time-based order to search Notes in for `consecutive` search",
|
||||
"enum": [
|
||||
"ascending",
|
||||
"descending"
|
||||
],
|
||||
"examples": [
|
||||
"descending"
|
||||
],
|
||||
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<value>\\d+)\\s*(?<percent>%?)\\s*(?<extra>asc.*|desc.*)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"search": {
|
||||
"default": "current",
|
||||
"description": "* If `current` then only the most recent note is checked\n* If `consecutive` then `count` number of `type` notes must be found in a row, based on `order` direction\n* If `total` then `count` number of `type` must be found within all notes",
|
||||
"description": "How to test the notes for this Author\n\n### current\n\nOnly the most recent note is checked for `type`\n\n### total\n\nThe `count` comparison of `type` must be found within all notes\n\n* EX `count: > 3` => Must have more than 3 notes of `type`, total\n* EX `count: <= 25%` => Must have 25% or less of notes of `type`, total\n\n### consecutive\n\nThe `count` **number** of `type` notes must be found in a row.\n\nYou may also specify the time-based order in which to search the notes by specifying `ascending (asc)` or `descending (desc)` in the `count` value. Default is `descending`\n\n* EX `count: >= 3` => Must have 3 or more notes of `type` consecutively, in descending order\n* EX `count: < 2` => Must have less than 2 notes of `type` consecutively, in descending order\n* EX `count: > 4 asc` => Must have greater than 4 notes of `type` consecutively, in ascending order",
|
||||
"enum": [
|
||||
"consecutive",
|
||||
"current",
|
||||
@@ -1849,7 +1827,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "User Note type key",
|
||||
"description": "User Note type key to search for",
|
||||
"examples": [
|
||||
"spamwarn"
|
||||
],
|
||||
|
||||
@@ -85,12 +85,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": "10%",
|
||||
"description": "The number or percentage to trigger this rule at\n\n* If `threshold` is a `number` then it is the absolute number of attribution instances to trigger at\n* If `threshold` is a `string` with percentage (EX `40%`) then it is the percentage of the total (see `lookAt`) this attribution must reach to trigger",
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
]
|
||||
"default": "> 10%",
|
||||
"description": "A string containing a comparison operator and a value to compare comments against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 12` => greater than 12 activities originate from same attribution\n* EX `<= 10%` => less than 10% of all Activities have the same attribution",
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
},
|
||||
"thresholdOn": {
|
||||
"default": "all",
|
||||
@@ -868,9 +866,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": 5,
|
||||
"default": ">= 5",
|
||||
"description": "The number of repeat submissions that will trigger the rule",
|
||||
"type": "number"
|
||||
"type": "string"
|
||||
},
|
||||
"useSubmissionAsReference": {
|
||||
"default": true,
|
||||
@@ -908,15 +906,6 @@
|
||||
"description": "At least one count property must be present. If both are present then either can trigger the rule",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "The number of activities in each subreddit from the list that will trigger this rule",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"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": [
|
||||
@@ -931,14 +920,14 @@
|
||||
"minItems": 2,
|
||||
"type": "array"
|
||||
},
|
||||
"totalCount": {
|
||||
"default": 1,
|
||||
"description": "The total number of activities across all listed subreddits that will trigger this rule",
|
||||
"threshold": {
|
||||
"default": ">= 1",
|
||||
"description": "A string containing a comparison operator and a value to compare recent activities against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 3` => greater than 3 activities found in the listed subreddits\n* EX `<= 75%` => number of Activities in the subreddits listed are equal to or less than 75% of all Activities\n\n**Note:** If you use percentage comparison here as well as `useSubmissionAsReference` then \"all Activities\" is only pertains to Activities that had the Link of the Submission, rather than all Activities from this window.",
|
||||
"examples": [
|
||||
1
|
||||
">= 1"
|
||||
],
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -992,28 +981,17 @@
|
||||
"UserNoteCriteria": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`",
|
||||
"default": ">= 1",
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`\n\nA string containing a comparison operator and/or a value to compare number of occurrences against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign] [ascending|descending]`",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"type": "number"
|
||||
},
|
||||
"order": {
|
||||
"default": "descending",
|
||||
"description": "Time-based order to search Notes in for `consecutive` search",
|
||||
"enum": [
|
||||
"ascending",
|
||||
"descending"
|
||||
],
|
||||
"examples": [
|
||||
"descending"
|
||||
],
|
||||
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<value>\\d+)\\s*(?<percent>%?)\\s*(?<extra>asc.*|desc.*)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"search": {
|
||||
"default": "current",
|
||||
"description": "* If `current` then only the most recent note is checked\n* If `consecutive` then `count` number of `type` notes must be found in a row, based on `order` direction\n* If `total` then `count` number of `type` must be found within all notes",
|
||||
"description": "How to test the notes for this Author\n\n### current\n\nOnly the most recent note is checked for `type`\n\n### total\n\nThe `count` comparison of `type` must be found within all notes\n\n* EX `count: > 3` => Must have more than 3 notes of `type`, total\n* EX `count: <= 25%` => Must have 25% or less of notes of `type`, total\n\n### consecutive\n\nThe `count` **number** of `type` notes must be found in a row.\n\nYou may also specify the time-based order in which to search the notes by specifying `ascending (asc)` or `descending (desc)` in the `count` value. Default is `descending`\n\n* EX `count: >= 3` => Must have 3 or more notes of `type` consecutively, in descending order\n* EX `count: < 2` => Must have less than 2 notes of `type` consecutively, in descending order\n* EX `count: > 4 asc` => Must have greater than 4 notes of `type` consecutively, in ascending order",
|
||||
"enum": [
|
||||
"consecutive",
|
||||
"current",
|
||||
@@ -1025,7 +1003,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "User Note type key",
|
||||
"description": "User Note type key to search for",
|
||||
"examples": [
|
||||
"spamwarn"
|
||||
],
|
||||
|
||||
@@ -65,12 +65,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": "10%",
|
||||
"description": "The number or percentage to trigger this rule at\n\n* If `threshold` is a `number` then it is the absolute number of attribution instances to trigger at\n* If `threshold` is a `string` with percentage (EX `40%`) then it is the percentage of the total (see `lookAt`) this attribution must reach to trigger",
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
]
|
||||
"default": "> 10%",
|
||||
"description": "A string containing a comparison operator and a value to compare comments against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 12` => greater than 12 activities originate from same attribution\n* EX `<= 10%` => less than 10% of all Activities have the same attribution",
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
},
|
||||
"thresholdOn": {
|
||||
"default": "all",
|
||||
@@ -848,9 +846,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"default": 5,
|
||||
"default": ">= 5",
|
||||
"description": "The number of repeat submissions that will trigger the rule",
|
||||
"type": "number"
|
||||
"type": "string"
|
||||
},
|
||||
"useSubmissionAsReference": {
|
||||
"default": true,
|
||||
@@ -888,15 +886,6 @@
|
||||
"description": "At least one count property must be present. If both are present then either can trigger the rule",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "The number of activities in each subreddit from the list that will trigger this rule",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"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": [
|
||||
@@ -911,14 +900,14 @@
|
||||
"minItems": 2,
|
||||
"type": "array"
|
||||
},
|
||||
"totalCount": {
|
||||
"default": 1,
|
||||
"description": "The total number of activities across all listed subreddits that will trigger this rule",
|
||||
"threshold": {
|
||||
"default": ">= 1",
|
||||
"description": "A string containing a comparison operator and a value to compare recent activities against\n\nThe syntax is `[< OR > OR <= OR >=] [number][?percent sign]`\n\n* EX `> 3` => greater than 3 activities found in the listed subreddits\n* EX `<= 75%` => number of Activities in the subreddits listed are equal to or less than 75% of all Activities\n\n**Note:** If you use percentage comparison here as well as `useSubmissionAsReference` then \"all Activities\" is only pertains to Activities that had the Link of the Submission, rather than all Activities from this window.",
|
||||
"examples": [
|
||||
1
|
||||
">= 1"
|
||||
],
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -972,28 +961,17 @@
|
||||
"UserNoteCriteria": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"default": 1,
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`",
|
||||
"default": ">= 1",
|
||||
"description": "Number of occurrences of this type. Ignored if `search` is `current`\n\nA string containing a comparison operator and/or a value to compare number of occurrences against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign] [ascending|descending]`",
|
||||
"examples": [
|
||||
1
|
||||
],
|
||||
"type": "number"
|
||||
},
|
||||
"order": {
|
||||
"default": "descending",
|
||||
"description": "Time-based order to search Notes in for `consecutive` search",
|
||||
"enum": [
|
||||
"ascending",
|
||||
"descending"
|
||||
],
|
||||
"examples": [
|
||||
"descending"
|
||||
],
|
||||
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<value>\\d+)\\s*(?<percent>%?)\\s*(?<extra>asc.*|desc.*)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"search": {
|
||||
"default": "current",
|
||||
"description": "* If `current` then only the most recent note is checked\n* If `consecutive` then `count` number of `type` notes must be found in a row, based on `order` direction\n* If `total` then `count` number of `type` must be found within all notes",
|
||||
"description": "How to test the notes for this Author\n\n### current\n\nOnly the most recent note is checked for `type`\n\n### total\n\nThe `count` comparison of `type` must be found within all notes\n\n* EX `count: > 3` => Must have more than 3 notes of `type`, total\n* EX `count: <= 25%` => Must have 25% or less of notes of `type`, total\n\n### consecutive\n\nThe `count` **number** of `type` notes must be found in a row.\n\nYou may also specify the time-based order in which to search the notes by specifying `ascending (asc)` or `descending (desc)` in the `count` value. Default is `descending`\n\n* EX `count: >= 3` => Must have 3 or more notes of `type` consecutively, in descending order\n* EX `count: < 2` => Must have less than 2 notes of `type` consecutively, in descending order\n* EX `count: > 4 asc` => Must have greater than 4 notes of `type` consecutively, in ascending order",
|
||||
"enum": [
|
||||
"consecutive",
|
||||
"current",
|
||||
@@ -1005,7 +983,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "User Note type key",
|
||||
"description": "User Note type key to search for",
|
||||
"examples": [
|
||||
"spamwarn"
|
||||
],
|
||||
|
||||
@@ -13,15 +13,16 @@ import {
|
||||
TypedActivityStates
|
||||
} from "../Common/interfaces";
|
||||
import {
|
||||
compareDurationValue,
|
||||
compareDurationValue, comparisonTextOp,
|
||||
isActivityWindowCriteria,
|
||||
normalizeName, parseDuration,
|
||||
parseDurationComparison,
|
||||
parseDurationComparison, parseGenericValueOrPercentComparison,
|
||||
truncateStringToLength
|
||||
} from "../util";
|
||||
import UserNotes from "../Subreddit/UserNotes";
|
||||
import {Logger} from "winston";
|
||||
import InvalidRegexError from "./InvalidRegexError";
|
||||
import SimpleError from "./SimpleError";
|
||||
|
||||
export const BOT_LINK = 'https://www.reddit.com/r/ContextModBot/comments/o1dugk/introduction_to_contextmodbot_and_rcb';
|
||||
|
||||
@@ -266,7 +267,8 @@ export const renderContent = async (template: string, data: (Submission | Commen
|
||||
|
||||
export const testAuthorCriteria = async (item: (Comment | Submission), authorOpts: AuthorCriteria, include = true, userNotes: UserNotes) => {
|
||||
// @ts-ignore
|
||||
const author: RedditUser = await item.author;
|
||||
const author: RedditUser = await item.author.fetch();
|
||||
debugger;
|
||||
for (const k of Object.keys(authorOpts)) {
|
||||
// @ts-ignore
|
||||
if (authorOpts[k] !== undefined) {
|
||||
@@ -336,7 +338,9 @@ export const testAuthorCriteria = async (item: (Comment | Submission), authorOpt
|
||||
const notes = await userNotes.getUserNotes(item.author);
|
||||
const notePass = () => {
|
||||
for (const noteCriteria of authorOpts[k] as UserNoteCriteria[]) {
|
||||
const {count = 1, order = 'descending', search = 'current', type} = noteCriteria;
|
||||
const {count = '>= 1', search = 'current', type} = noteCriteria;
|
||||
const {value, operator, isPercent, extra = ''} = parseGenericValueOrPercentComparison(count);
|
||||
const order = extra.includes('asc') ? 'ascending' : 'descending';
|
||||
switch (search) {
|
||||
case 'current':
|
||||
if (notes.length > 0 && notes[notes.length - 1].noteType === type) {
|
||||
@@ -356,13 +360,20 @@ export const testAuthorCriteria = async (item: (Comment | Submission), authorOpt
|
||||
} else {
|
||||
currCount = 0;
|
||||
}
|
||||
if (currCount >= count) {
|
||||
if(isPercent) {
|
||||
throw new SimpleError(`When comparing UserNotes with 'consecutive' search 'count' cannot be a percentage. Given: ${count}`);
|
||||
}
|
||||
if (comparisonTextOp(currCount, operator, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'total':
|
||||
if (notes.filter(x => x.noteType === type).length >= count) {
|
||||
if(isPercent) {
|
||||
if(comparisonTextOp(notes.filter(x => x.noteType === type).length / notes.length, operator, value/100)) {
|
||||
return true;
|
||||
}
|
||||
} else if(comparisonTextOp(notes.filter(x => x.noteType === type).length, operator, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
54
src/util.ts
54
src/util.ts
@@ -207,21 +207,6 @@ export const createAjvFactory = (logger: Logger) => {
|
||||
return new Ajv({logger: logger, verbose: true, strict: "log", allowUnionTypes: true});
|
||||
}
|
||||
|
||||
export const comparisonTextOp = (val1: number, strOp: string, val2: number): boolean => {
|
||||
switch (strOp) {
|
||||
case '>':
|
||||
return val1 > val2;
|
||||
case '>=':
|
||||
return val1 >= val2;
|
||||
case '<':
|
||||
return val1 < val2;
|
||||
case '<=':
|
||||
return val1 <= val2;
|
||||
default:
|
||||
throw new Error(`${strOp} was not a recognized operator`);
|
||||
}
|
||||
}
|
||||
|
||||
export const percentFromString = (str: string): number => {
|
||||
const n = Number.parseInt(str.replace('%', ''));
|
||||
if(Number.isNaN(n)) {
|
||||
@@ -385,9 +370,42 @@ export const parseFromJsonOrYamlToObject = (content: string): [object?, Error?,
|
||||
return [obj, jsonErr, yamlErr];
|
||||
}
|
||||
|
||||
export const comparisonTextOp = (val1: number, strOp: string, val2: number): boolean => {
|
||||
switch (strOp) {
|
||||
case '>':
|
||||
return val1 > val2;
|
||||
case '>=':
|
||||
return val1 >= val2;
|
||||
case '<':
|
||||
return val1 < val2;
|
||||
case '<=':
|
||||
return val1 <= val2;
|
||||
default:
|
||||
throw new Error(`${strOp} was not a recognized operator`);
|
||||
}
|
||||
}
|
||||
|
||||
const GENERIC_VALUE_COMPARISON = /^\s*(?<opStr>>|>=|<|<=)\s*(?<value>\d+)(?<extra>\s+.*)*$/
|
||||
const GENERIC_VALUE_COMPARISON_URL = 'https://regexr.com/60dq4';
|
||||
export const parseGenericValueComparison = (val: string): GenericComparison => {
|
||||
const matches = val.match(GENERIC_VALUE_COMPARISON);
|
||||
if (matches === null) {
|
||||
throw new InvalidRegexError(GENERIC_VALUE_COMPARISON, val, GENERIC_VALUE_COMPARISON_URL)
|
||||
}
|
||||
const groups = matches.groups as any;
|
||||
|
||||
return {
|
||||
operator: groups.opStr as StringOperator,
|
||||
value: Number.parseFloat(groups.value),
|
||||
isPercent: false,
|
||||
extra: groups.extra,
|
||||
displayText: `${groups.opStr} ${groups.number}`
|
||||
}
|
||||
}
|
||||
|
||||
const GENERIC_VALUE_PERCENT_COMPARISON = /^\s*(?<opStr>>|>=|<|<=)\s*(?<value>\d+)\s*(?<percent>%?)(?<extra>.*)$/
|
||||
const GENERIC_VALUE_PERCENT_COMPARISON_URL = 'https://regexr.com/60a16';
|
||||
export const parseGenericComparison = (val: string): GenericComparison => {
|
||||
export const parseGenericValueOrPercentComparison = (val: string): GenericComparison => {
|
||||
const matches = val.match(GENERIC_VALUE_PERCENT_COMPARISON);
|
||||
if (matches === null) {
|
||||
throw new InvalidRegexError(GENERIC_VALUE_PERCENT_COMPARISON, val, GENERIC_VALUE_PERCENT_COMPARISON_URL)
|
||||
@@ -396,8 +414,8 @@ export const parseGenericComparison = (val: string): GenericComparison => {
|
||||
|
||||
return {
|
||||
operator: groups.opStr as StringOperator,
|
||||
value: Number.parseFloat(groups.number),
|
||||
isPercent: groups.percent !== undefined,
|
||||
value: Number.parseFloat(groups.value),
|
||||
isPercent: groups.percent !== '',
|
||||
extra: groups.extra,
|
||||
displayText: `${groups.opStr} ${groups.number}${groups.percent === undefined ? '': '%'}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user