Compare commits

..

1 Commits

Author SHA1 Message Date
FoxxMD
381976d6af feat(modlog): Implement normalizing raw modlog data
Refactor ModAction to use data from either mod notes or modlog
2022-10-03 11:31:35 -04:00
15 changed files with 182 additions and 284 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,2 @@
github: [FoxxMD]
patreon: FoxxMD
custom: ["bitcoincash:qqmpsh365r8n9jhp4p8ks7f7qdr7203cws4kmkmr8q"]

View File

@@ -40,7 +40,7 @@
// for this to pass the Author of the Submission must not have the flair "Supreme Memer" and have the name "user1" or "user2"
{
"flairText": ["Supreme Memer"],
"name": ["user1","user2"]
"names": ["user1","user2"]
},
{
// for this to pass the Author of the Submission must not have the flair "Decent Memer"

View File

@@ -30,7 +30,7 @@ runs:
# for this to pass the Author of the Submission must not have the flair "Supreme Memer" and have the name "user1" or "user2"
- flairText:
- Supreme Memer
name:
names:
- user1
- user2
# for this to pass the Author of the Submission must not have the flair "Decent Memer"

View File

@@ -5,45 +5,9 @@ The **History** rule can check an Author's submission/comment statistics over a
* Submission total or percentage of All Activity
* Comment total or percentage of all Activity
* Comments made as OP (commented in their own Submission) total or percentage of all Comments
* Ratio of activities against another window of activities
Consult the [schema](https://json-schema.app/view/%23%2Fdefinitions%2FHistoryJSONConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fcontext-mod%2Fmaster%2Fsrc%2FSchema%2FApp.json) for a complete reference of the rule's properties.
## Ratio
Use the `ratio` property in Criteria to test the [number of activities](/docs/subreddit/activitiesWindow.md) found in the parent criteria against the number of activities from _another_ [activity window](/docs/subreddit/activitiesWindow.md) defined in the ratio.
Example:
```yaml
- kind: history
criteria:
# "parent" criteria, returns all activities, in the last 100 from user's history, that occurred in r/mealtimevideos
- window:
count: 100
filterOn:
post:
subreddits:
include:
- mealtimevideos
ratio:
# "ratio" criteria, returns all activities, in the last 100 from user's history, that occurred in r/redditdev
window:
count: 100
filterOn:
post:
subreddits:
include:
- redditdev
# test (number of parent criteria activities) / (number of ratio critieria activities)
threshold: '> 1.2'
```
`threshold` may be a number or percentage `(number * 100)`
* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities
* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities
### Examples
* Low Comment Engagement [YAML](/docs/subreddit/componentscomponents/history/lowEngagement.yaml) | [JSON](/docs/subreddit/componentscomponents/history/lowEngagement.json5) - Check if Author is submitting much more than they comment.

View File

@@ -41,7 +41,7 @@ export type RelativeDateTimeMatch = string;
* * EX `> 100` => greater than 100
* * EX `<= 75%` => less than or equal to 75%
*
* @pattern ^\s*(>|>=|<|<=)\s*((?:\d+)(?:(?:(?:.|,)\d+)+)?)\s*(%?)(.*)$
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(%?)(.*)$
* */
export type CompareValueOrPercent = string;
export type StringOperator = '>' | '>=' | '<' | '<=';
@@ -395,3 +395,82 @@ export interface RuleResultsTemplateData {
export interface GenericContentTemplateData extends BaseTemplateData, Partial<RuleResultsTemplateData>, Partial<ActionResultsTemplateData> {
item?: (SubmissionTemplateData | CommentTemplateData)
}
export type ModerationActionType =
'banuser'
| 'unbanuser'
| 'spamlink'
| 'removelink'
| 'approvelink'
| 'spamcomment'
| 'removecomment'
| 'approvecomment'
| 'addmoderator'
| 'showcomment'
| 'invitemoderator'
| 'uninvitemoderator'
| 'acceptmoderatorinvite'
| 'removemoderator'
| 'addcontributor'
| 'removecontributor'
| 'editsettings'
| 'editflair'
| 'distinguish'
| 'marknsfw'
| 'wikibanned'
| 'wikicontributor'
| 'wikiunbanned'
| 'wikipagelisted'
| 'removewikicontributor'
| 'wikirevise'
| 'wikipermlevel'
| 'ignorereports'
| 'unignorereports'
| 'setpermissions'
| 'setsuggestedsort'
| 'sticky'
| 'unsticky'
| 'setcontestmode'
| 'unsetcontestmode'
| 'lock'
| 'unlock'
| 'muteuser'
| 'unmuteuser'
| 'createrule'
| 'editrule'
| 'reorderrules'
| 'deleterule'
| 'spoiler'
| 'unspoiler'
| 'modmail_enrollment'
| 'community_styling'
| 'community_widgets'
| 'markoriginalcontent'
| 'collections'
| 'events'
| 'hidden_award'
| 'add_community_topics'
| 'remove_community_topics'
| 'create_scheduled_post'
| 'edit_scheduled_post'
| 'delete_scheduled_post'
| 'submit_scheduled_post'
| 'edit_post_requirements'
| 'invitesubscriber'
| 'submit_content_rating_survey'
| 'adjust_post_crowd_control_level'
| 'enable_post_crowd_control_filter'
| 'disable_post_crowd_control_filter'
| 'deleteoverriddenclassification'
| 'overrideclassification'
| 'reordermoderators'
| 'snoozereports'
| 'unsnoozereports'
| 'addnote'
| 'deletenote'
| 'addremovalreason'
| 'createremovalreason'
| 'updateremovalreason'
| 'deleteremovalreason';
export const moderatorActionTypes = ['banuser', 'unbanuser', 'spamlink', 'removelink', 'approvelink', 'spamcomment', 'removecomment', 'approvecomment', 'addmoderator', 'showcomment', 'invitemoderator', 'uninvitemoderator', 'acceptmoderatorinvite', 'removemoderator', 'addcontributor', 'removecontributor', 'editsettings', 'editflair', 'distinguish', 'marknsfw', 'wikibanned', 'wikicontributor', 'wikiunbanned', 'wikipagelisted', 'removewikicontributor', 'wikirevise', 'wikipermlevel', 'ignorereports', 'unignorereports', 'setpermissions', 'setsuggestedsort', 'sticky', 'unsticky', 'setcontestmode', 'unsetcontestmode', 'lock', 'unlock', 'muteuser', 'unmuteuser', 'createrule', 'editrule', 'reorderrules', 'deleterule', 'spoiler', 'unspoiler', 'modmail_enrollment', 'community_styling', 'community_widgets', 'markoriginalcontent', 'collections', 'events', 'hidden_award', 'add_community_topics', 'remove_community_topics', 'create_scheduled_post', 'edit_scheduled_post', 'delete_scheduled_post', 'submit_scheduled_post', 'edit_post_requirements', 'invitesubscriber', 'submit_content_rating_survey', 'adjust_post_crowd_control_level', 'enable_post_crowd_control_filter', 'disable_post_crowd_control_filter', 'deleteoverriddenclassification', 'overrideclassification', 'reordermoderators', 'snoozereports', 'unsnoozereports', 'addnote', 'deletenote', 'addremovalreason', 'createremovalreason', 'updateremovalreason', 'deleteremovalreason'];

View File

@@ -35,8 +35,8 @@ export const asGenericComparison = (val: any): val is GenericComparison => {
return typeof val === 'object' && 'value' in val;
}
export const GENERIC_VALUE_COMPARISON = /^\s*(?<opStr>>|>=|<|<=)\s*(?<value>-?(?:\d+)(?:(?:(?:.|,)\d+)+)?)(?<extra>\s+.*)*$/
export const GENERIC_VALUE_COMPARISON_URL = 'https://regexr.com/6vama';
export const GENERIC_VALUE_COMPARISON = /^\s*(?<opStr>>|>=|<|<=)\s*(?<value>-?\d?\.?\d+)(?<extra>\s+.*)*$/
export const GENERIC_VALUE_COMPARISON_URL = 'https://regexr.com/60dq4';
export const parseGenericValueComparison = (val: string, options?: {
requireDuration?: boolean,
reg?: RegExp
@@ -107,8 +107,8 @@ export const parseGenericValueComparison = (val: string, options?: {
durationText,
}
}
const GENERIC_VALUE_PERCENT_COMPARISON = /^\s*(?<opStr>>|>=|<|<=)\s*(?<value>(?:\d+)(?:(?:(?:.|,)\d+)+)?)\s*(?<percent>%)?(?<extra>.*)$/
const GENERIC_VALUE_PERCENT_COMPARISON_URL = 'https://regexr.com/6valr';
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 parseGenericValueOrPercentComparison = (val: string, options?: {requireDuration: boolean}): GenericComparison => {
return parseGenericValueComparison(val, {...(options ?? {}), reg: GENERIC_VALUE_PERCENT_COMPARISON});
}

View File

@@ -45,4 +45,4 @@ export const filterCriteriaDefault: FilterCriteriaDefaults = {
export const defaultDataDir = path.resolve(__dirname, '../..');
export const defaultConfigFilenames = ['config.json', 'config.yaml'];
export const VERSION = '0.13.1';
export const VERSION = '0.12.2';

View File

@@ -81,23 +81,6 @@ export interface HistoryCriteria {
window: ActivityWindowConfig
ratio?: {
window: ActivityWindowConfig
/**
* A string containing a comparison operator and a value to compare number of parent criteria activities against number of "ratio" activities
*
* This comparison is always done as (number of parent criteria activities) / (number of ratio activities)
*
* The syntax is `(< OR > OR <= OR >=) <number>[percent sign]`
*
* * EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities
* * EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities
*
* @pattern ^\s*(>|>=|<|<=)\s*((?:\d+)(?:(?:(?:.|,)\d+)+)?)\s*(%?)(.*)$
* */
threshold: CompareValueOrPercent
}
/**
* The minimum number of **filtered** activities that must exist from the `window` results for this criteria to run
* @default 5
@@ -187,7 +170,7 @@ export class HistoryRule extends Rule {
for (const criteria of this.criteria) {
const {comment, window, submission, total, ratio, minActivityCount = 5} = criteria;
const {comment, window, submission, total, minActivityCount = 5} = criteria;
const {pre: activities, post: filteredActivities} = await this.resources.getAuthorActivitiesWithFilter(item.author, window);
@@ -268,24 +251,6 @@ export class HistoryRule extends Rule {
}
}
let foundRatio = undefined;
let ratioTrigger = undefined;
if(ratio !== undefined) {
const { window: ratioWindow, threshold: ratioThreshold } = ratio;
const {operator, value, isPercent, extra = ''} = parseGenericValueOrPercentComparison(ratioThreshold);
const ratioWindowConfig = windowConfigToWindowCriteria(ratioWindow);
const {post: ratioActivities} = await this.resources.getAuthorActivitiesWithFilter(item.author, ratioWindowConfig);
const ratioVal = filteredActivities.length / ratioActivities.length;
foundRatio = formatNumber(ratioVal);
if(isPercent) {
const per = value / 100;
ratioTrigger = comparisonTextOp(ratioVal, operator, per);
} else {
ratioTrigger = comparisonTextOp(ratioVal, operator, value);
}
}
const firstActivity = activities[0];
const lastActivity = activities[activities.length - 1];
@@ -298,13 +263,11 @@ export class HistoryRule extends Rule {
submissionTotal: fSubmissionTotal,
commentTotal: fCommentTotal,
opTotal: fOpTotal,
foundRatio,
filteredTotal: filteredActivities.length,
submissionTrigger,
commentTrigger,
totalTrigger,
ratioTrigger,
triggered: (submissionTrigger === undefined || submissionTrigger === true) && (commentTrigger === undefined || commentTrigger === true) && (totalTrigger === undefined || totalTrigger === true) && (ratioTrigger === undefined || ratioTrigger === true),
triggered: (submissionTrigger === undefined || submissionTrigger === true) && (commentTrigger === undefined || commentTrigger === true) && (totalTrigger === undefined || totalTrigger === true),
subredditBreakdown: getSubredditBreakdownByActivityType(!asOp ? filteredActivities : filteredActivities.filter(x => asSubmission(x) || x.is_submitter))
});
}
@@ -355,13 +318,11 @@ export class HistoryRule extends Rule {
submissionTotal,
commentTotal,
filteredTotal,
foundRatio,
opTotal,
criteria: {
comment,
submission,
total,
ratio,
window,
},
criteria,
@@ -369,7 +330,6 @@ export class HistoryRule extends Rule {
submissionTrigger,
commentTrigger,
totalTrigger,
ratioTrigger,
subredditBreakdown,
} = results;
@@ -378,7 +338,6 @@ export class HistoryRule extends Rule {
submissionTotal,
commentTotal,
filteredTotal,
foundRatio,
opTotal,
commentPercent: formatNumber((commentTotal/activityTotal)*100),
submissionPercent: formatNumber((submissionTotal/activityTotal)*100),
@@ -390,7 +349,6 @@ export class HistoryRule extends Rule {
submissionTrigger,
commentTrigger,
totalTrigger,
ratioTrigger,
subredditBreakdown
};
@@ -398,7 +356,6 @@ export class HistoryRule extends Rule {
let totalSummary;
let submissionSummary;
let commentSummary;
let ratioSummary;
if(total !== undefined) {
const {operator, value, isPercent, displayText} = parseGenericValueOrPercentComparison(total);
const suffix = !isPercent ? 'Items' : `(${formatNumber((filteredTotal/activityTotal)*100)}%) of ${activityTotal} Total`;
@@ -423,13 +380,6 @@ export class HistoryRule extends Rule {
data.commentSummary = commentSummary;
thresholdSummary.push(commentSummary);
}
if(ratio !== undefined) {
const {threshold} = ratio;
const {operator, value, isPercent, displayText, extra = ''} = parseGenericValueOrPercentComparison(threshold);
ratioSummary = `${includePassFailSymbols ? `${submissionTrigger ? PASS : FAIL} ` : ''}Activity Ratio of (${foundRatio}) ${ratioTrigger ? 'passed' : 'did not pass'} test of ${displayText}`;
data.ratioSummary = ratioSummary;
thresholdSummary.push(ratioSummary);
}
data.thresholdSummary = thresholdSummary.join(' and ');

View File

@@ -2904,40 +2904,6 @@
"name": {
"type": "string"
},
"ratio": {
"properties": {
"threshold": {
"description": "A string containing a comparison operator and a value to compare number of parent criteria activities against number of \"ratio\" activities\n\nThis comparison is always done as (number of parent criteria activities) / (number of ratio activities)\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities\n* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*((?:\\d+)(?:(?:(?:.|,)\\d+)+)?)\\s*(%?)(.*)$",
"type": "string"
},
"window": {
"anyOf": [
{
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
"string",
"number"
]
}
],
"description": "A value to define the range of Activities to retrieve.\n\nAcceptable values:\n\n**`ActivityWindowCriteria` object**\n\nAllows specify multiple range properties and more specific behavior\n\n**A `number` of Activities to retrieve**\n\n* EX `100` => 100 Activities\n\n*****\n\nAny of the below values that specify the amount of time to subtract from `NOW` to create a time range IE `NOW <---> [duration] ago`\n\nAcceptable values:\n\n**A `string` consisting of a value and a [Day.js](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) time UNIT**\n\n* EX `9 days` => Range is `NOW <---> 9 days ago`\n\n**A [Day.js](https://day.js.org/docs/en/durations/creating) `object`**\n\n* EX `{\"days\": 90, \"minutes\": 15}` => Range is `NOW <---> 90 days and 15 minutes ago`\n\n**An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) `string`**\n\n* EX `PT15M` => 15 minutes => Range is `NOW <----> 15 minutes ago`",
"examples": [
"90 days"
]
}
},
"required": [
"threshold",
"window"
],
"type": "object"
},
"submission": {
"description": "A string containing a comparison operator and a value to compare **filtered** (using `include` or `exclude`, if present) submissions against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 filtered submissions\n* EX `<= 75%` => filtered submissions are equal to or less than 75% of unfiltered Activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",

View File

@@ -2618,40 +2618,6 @@
"name": {
"type": "string"
},
"ratio": {
"properties": {
"threshold": {
"description": "A string containing a comparison operator and a value to compare number of parent criteria activities against number of \"ratio\" activities\n\nThis comparison is always done as (number of parent criteria activities) / (number of ratio activities)\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities\n* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*((?:\\d+)(?:(?:(?:.|,)\\d+)+)?)\\s*(%?)(.*)$",
"type": "string"
},
"window": {
"anyOf": [
{
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
"string",
"number"
]
}
],
"description": "A value to define the range of Activities to retrieve.\n\nAcceptable values:\n\n**`ActivityWindowCriteria` object**\n\nAllows specify multiple range properties and more specific behavior\n\n**A `number` of Activities to retrieve**\n\n* EX `100` => 100 Activities\n\n*****\n\nAny of the below values that specify the amount of time to subtract from `NOW` to create a time range IE `NOW <---> [duration] ago`\n\nAcceptable values:\n\n**A `string` consisting of a value and a [Day.js](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) time UNIT**\n\n* EX `9 days` => Range is `NOW <---> 9 days ago`\n\n**A [Day.js](https://day.js.org/docs/en/durations/creating) `object`**\n\n* EX `{\"days\": 90, \"minutes\": 15}` => Range is `NOW <---> 90 days and 15 minutes ago`\n\n**An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) `string`**\n\n* EX `PT15M` => 15 minutes => Range is `NOW <----> 15 minutes ago`",
"examples": [
"90 days"
]
}
},
"required": [
"threshold",
"window"
],
"type": "object"
},
"submission": {
"description": "A string containing a comparison operator and a value to compare **filtered** (using `include` or `exclude`, if present) submissions against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 filtered submissions\n* EX `<= 75%` => filtered submissions are equal to or less than 75% of unfiltered Activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",

View File

@@ -1467,40 +1467,6 @@
"name": {
"type": "string"
},
"ratio": {
"properties": {
"threshold": {
"description": "A string containing a comparison operator and a value to compare number of parent criteria activities against number of \"ratio\" activities\n\nThis comparison is always done as (number of parent criteria activities) / (number of ratio activities)\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities\n* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*((?:\\d+)(?:(?:(?:.|,)\\d+)+)?)\\s*(%?)(.*)$",
"type": "string"
},
"window": {
"anyOf": [
{
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
"string",
"number"
]
}
],
"description": "A value to define the range of Activities to retrieve.\n\nAcceptable values:\n\n**`ActivityWindowCriteria` object**\n\nAllows specify multiple range properties and more specific behavior\n\n**A `number` of Activities to retrieve**\n\n* EX `100` => 100 Activities\n\n*****\n\nAny of the below values that specify the amount of time to subtract from `NOW` to create a time range IE `NOW <---> [duration] ago`\n\nAcceptable values:\n\n**A `string` consisting of a value and a [Day.js](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) time UNIT**\n\n* EX `9 days` => Range is `NOW <---> 9 days ago`\n\n**A [Day.js](https://day.js.org/docs/en/durations/creating) `object`**\n\n* EX `{\"days\": 90, \"minutes\": 15}` => Range is `NOW <---> 90 days and 15 minutes ago`\n\n**An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) `string`**\n\n* EX `PT15M` => 15 minutes => Range is `NOW <----> 15 minutes ago`",
"examples": [
"90 days"
]
}
},
"required": [
"threshold",
"window"
],
"type": "object"
},
"submission": {
"description": "A string containing a comparison operator and a value to compare **filtered** (using `include` or `exclude`, if present) submissions against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 filtered submissions\n* EX `<= 75%` => filtered submissions are equal to or less than 75% of unfiltered Activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",

View File

@@ -1432,40 +1432,6 @@
"name": {
"type": "string"
},
"ratio": {
"properties": {
"threshold": {
"description": "A string containing a comparison operator and a value to compare number of parent criteria activities against number of \"ratio\" activities\n\nThis comparison is always done as (number of parent criteria activities) / (number of ratio activities)\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities\n* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*((?:\\d+)(?:(?:(?:.|,)\\d+)+)?)\\s*(%?)(.*)$",
"type": "string"
},
"window": {
"anyOf": [
{
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
"string",
"number"
]
}
],
"description": "A value to define the range of Activities to retrieve.\n\nAcceptable values:\n\n**`ActivityWindowCriteria` object**\n\nAllows specify multiple range properties and more specific behavior\n\n**A `number` of Activities to retrieve**\n\n* EX `100` => 100 Activities\n\n*****\n\nAny of the below values that specify the amount of time to subtract from `NOW` to create a time range IE `NOW <---> [duration] ago`\n\nAcceptable values:\n\n**A `string` consisting of a value and a [Day.js](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) time UNIT**\n\n* EX `9 days` => Range is `NOW <---> 9 days ago`\n\n**A [Day.js](https://day.js.org/docs/en/durations/creating) `object`**\n\n* EX `{\"days\": 90, \"minutes\": 15}` => Range is `NOW <---> 90 days and 15 minutes ago`\n\n**An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) `string`**\n\n* EX `PT15M` => 15 minutes => Range is `NOW <----> 15 minutes ago`",
"examples": [
"90 days"
]
}
},
"required": [
"threshold",
"window"
],
"type": "object"
},
"submission": {
"description": "A string containing a comparison operator and a value to compare **filtered** (using `include` or `exclude`, if present) submissions against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 filtered submissions\n* EX `<= 75%` => filtered submissions are equal to or less than 75% of unfiltered Activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",

View File

@@ -2685,40 +2685,6 @@
"name": {
"type": "string"
},
"ratio": {
"properties": {
"threshold": {
"description": "A string containing a comparison operator and a value to compare number of parent criteria activities against number of \"ratio\" activities\n\nThis comparison is always done as (number of parent criteria activities) / (number of ratio activities)\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 1.2` => There are 1.2 activities from parent criteria for every 1 ratio activities\n* EX `<= 75%` => There are equal to or less than 0.75 activities from parent criteria for every 1 ratio activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*((?:\\d+)(?:(?:(?:.|,)\\d+)+)?)\\s*(%?)(.*)$",
"type": "string"
},
"window": {
"anyOf": [
{
"$ref": "#/definitions/DurationObject"
},
{
"$ref": "#/definitions/FullActivityWindowConfig"
},
{
"type": [
"string",
"number"
]
}
],
"description": "A value to define the range of Activities to retrieve.\n\nAcceptable values:\n\n**`ActivityWindowCriteria` object**\n\nAllows specify multiple range properties and more specific behavior\n\n**A `number` of Activities to retrieve**\n\n* EX `100` => 100 Activities\n\n*****\n\nAny of the below values that specify the amount of time to subtract from `NOW` to create a time range IE `NOW <---> [duration] ago`\n\nAcceptable values:\n\n**A `string` consisting of a value and a [Day.js](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) time UNIT**\n\n* EX `9 days` => Range is `NOW <---> 9 days ago`\n\n**A [Day.js](https://day.js.org/docs/en/durations/creating) `object`**\n\n* EX `{\"days\": 90, \"minutes\": 15}` => Range is `NOW <---> 90 days and 15 minutes ago`\n\n**An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) `string`**\n\n* EX `PT15M` => 15 minutes => Range is `NOW <----> 15 minutes ago`",
"examples": [
"90 days"
]
}
},
"required": [
"threshold",
"window"
],
"type": "object"
},
"submission": {
"description": "A string containing a comparison operator and a value to compare **filtered** (using `include` or `exclude`, if present) submissions against\n\nThe syntax is `(< OR > OR <= OR >=) <number>[percent sign]`\n\n* EX `> 100` => greater than 100 filtered submissions\n* EX `<= 75%` => filtered submissions are equal to or less than 75% of unfiltered Activities",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",

View File

@@ -1,39 +1,111 @@
import {Submission, RedditUser, Comment, Subreddit, PrivateMessage} from "snoowrap/dist/objects"
import {generateSnoowrapEntityFromRedditThing, parseRedditFullname} from "../../util"
import Snoowrap from "snoowrap";
import {ModerationActionType} from "../../Common/Infrastructure/Atomic";
//import {ExtendedSnoowrap} from "../../Utils/SnoowrapClients";
export interface ModActionRaw {
action?: string | null
action?: ModerationActionType | null
reddit_id?: string | null
details?: string | null
description?: string | null
}
export interface ModActionRawNormalized extends ModActionRaw {
createdBy?: RedditUser | Subreddit
subreddit: Subreddit
}
export interface ModLogRaw {
id: string
mod_id36: string // wtf
mod: string // name of moderator that performed the action
target_fullname: string // ThingID IE t3_wuywlr
target_author: string
details: string // flair_edit
action: ModerationActionType
description: string
target_body: string
subreddit_name_prefixed: string
subreddit: Subreddit // proxy object
created_utc: number
}
export class ModAction {
action?: string
action?: ModerationActionType
actedOn?: RedditUser | Submission | Comment | Subreddit | PrivateMessage
details?: string
description?: string
createdBy?: RedditUser | Subreddit
subreddit?: Subreddit
constructor(data: ModActionRaw | undefined, client: Snoowrap) {
const {
action,
reddit_id,
details,
description
} = data || {};
this.action = action !== null ? action : undefined;
this.details = details !== null ? details : undefined;
this.description = description !== null ? description : undefined;
constructor(data: ModActionRawNormalized | ModLogRaw | undefined, client: Snoowrap, subreddit?: Subreddit) {
if(data !== undefined) {
const {
action,
details,
description
} = data || {};
if (reddit_id !== null && reddit_id !== undefined) {
const thing = parseRedditFullname(reddit_id);
if (thing !== undefined) {
this.actedOn = generateSnoowrapEntityFromRedditThing(thing, client);
if(subreddit !== undefined) {
this.subreddit = subreddit;
}
if(asModActionRaw(data)) {
const {
reddit_id,
createdBy,
subreddit: subFromData
} = data as ModActionRawNormalized || {};
this.createdBy = createdBy;
if(this.subreddit === undefined) {
this.subreddit = subFromData;
}
if (reddit_id !== null && reddit_id !== undefined) {
const thing = parseRedditFullname(reddit_id);
if (thing !== undefined) {
this.actedOn = generateSnoowrapEntityFromRedditThing(thing, client);
}
}
} else {
const {
target_fullname,
target_author,
mod,
mod_id36,
subreddit: subFromData
} = data || {};
if (target_fullname !== null && target_fullname !== undefined) {
const thing = parseRedditFullname(target_fullname);
if (thing !== undefined) {
this.actedOn = generateSnoowrapEntityFromRedditThing(thing, client);
if (this.actedOn instanceof RedditUser) {
this.actedOn.name = target_author;
}
}
}
const author = parseRedditFullname(`t2_${mod_id36}`);
if(author !== undefined) {
this.createdBy = generateSnoowrapEntityFromRedditThing(author, client) as RedditUser;
if (this.createdBy instanceof RedditUser) {
this.createdBy.name = mod;
}
}
if(this.subreddit === undefined) {
this.subreddit = subFromData;
}
}
this.action = action !== null ? action : undefined;
this.details = details !== null ? details : undefined;
this.description = description !== null ? description : undefined;
}
}
toRaw(): ModActionRaw {
@@ -50,4 +122,8 @@ export class ModAction {
}
}
export const asModActionRaw = (data: any): data is ModActionRaw => {
return data !== null && 'reddit_id' in data;
}
export default ModAction;

View File

@@ -81,7 +81,7 @@ export class ModNote {
this.createdBy.name = data.operator;
}
this.action = new ModAction(data.mod_action_data, client);
this.action = new ModAction({...data.mod_action_data, createdBy: this.createdBy, subreddit: this.subreddit}, client);
if (this.action.actedOn instanceof RedditUser && this.action.actedOn.id === this.user.id) {
this.action.actedOn = this.user;
}