Skip to content

Commit 9790628

Browse files
committed
Merge branch 'edge'
2 parents 487f13f + d5e1cde commit 9790628

File tree

11 files changed

+352
-68
lines changed

11 files changed

+352
-68
lines changed

src/Author/Author.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {UserNoteCriteria} from "../Rule";
22
import {CompareValue, CompareValueOrPercent, DurationComparor} from "../Common/interfaces";
3+
import {parseStringToRegex} from "../util";
34

45
/**
56
* If present then these Author criteria are checked before running the rule. If criteria fails then the rule is skipped.
@@ -106,6 +107,17 @@ export interface AuthorCriteria {
106107
* This is determined by trying to retrieve the author's profile. If a 404 is returned it is likely they are shadowbanned
107108
* */
108109
shadowBanned?: boolean
110+
111+
/**
112+
* An (array of) string/regular expression to test contents of an Author's profile description against
113+
*
114+
* If no flags are specified then the **insensitive** flag is used by default
115+
*
116+
* If using an array then if **any** value in the array passes the description test passes
117+
*
118+
* @examples [["/test$/i", "look for this string literal"]]
119+
* */
120+
description?: string | string[]
109121
}
110122

111123
export class Author implements AuthorCriteria {
@@ -120,6 +132,7 @@ export class Author implements AuthorCriteria {
120132
totalKarma?: string;
121133
verified?: boolean;
122134
shadowBanned?: boolean;
135+
description?: string[];
123136

124137
constructor(options: AuthorCriteria) {
125138
this.name = options.name;
@@ -132,6 +145,7 @@ export class Author implements AuthorCriteria {
132145
this.linkKarma = options.linkKarma;
133146
this.totalKarma = options.totalKarma;
134147
this.shadowBanned = options.shadowBanned;
148+
this.description = options.description === undefined ? undefined : Array.isArray(options.description) ? options.description : [options.description];
135149
}
136150
}
137151

src/Common/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,9 @@ export interface SubmissionState extends ActivityState {
932932
link_flair_css_class?: string
933933
}
934934

935+
// properties calculated/derived by CM -- not provided as plain values by reddit
936+
export const cmActivityProperties = ['submissionState','score','reports','removed','deleted','filtered','age','title'];
937+
935938
/**
936939
* Different attributes a `Comment` can be in. Only include a property if you want to check it.
937940
* @examples [{"op": true, "removed": false}]

src/Rule/AttributionRule.ts

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {SubmissionRule, SubmissionRuleJSONConfig} from "./SubmissionRule";
2-
import {ActivityWindowType, DomainInfo, ReferenceSubmission} from "../Common/interfaces";
2+
import {ActivityWindowType, CommentState, DomainInfo, ReferenceSubmission, SubmissionState} from "../Common/interfaces";
33
import {Rule, RuleOptions, RuleResult} from "./index";
44
import Submission from "snoowrap/dist/objects/Submission";
55
import {getAttributionIdentifier} from "../Utils/SnoowrapUtils";
66
import dayjs from "dayjs";
77
import {
88
asSubmission,
9-
comparisonTextOp,
9+
comparisonTextOp, convertSubredditsRawToStrong,
1010
FAIL,
1111
formatNumber, getActivitySubredditName, isSubmission,
1212
parseGenericValueOrPercentComparison,
@@ -15,6 +15,7 @@ import {
1515
} from "../util";
1616
import { Comment } from "snoowrap/dist/objects";
1717
import SimpleError from "../Utils/SimpleError";
18+
import as from "async";
1819

1920

2021
export interface AttributionCriteria {
@@ -76,25 +77,41 @@ export interface AttributionCriteria {
7677
domainsCombined?: boolean,
7778

7879
/**
79-
* Only include Activities from this list of Subreddits (by name, case-insensitive)
80+
* When present, Activities WILL ONLY be counted if they are found in this list of Subreddits
8081
*
82+
* Each value in the list can be either:
8183
*
82-
* EX `["mealtimevideos","askscience"]`
83-
* @examples ["mealtimevideos","askscience"]
84-
* @minItems 1
84+
* * string (name of subreddit)
85+
* * regular expression to run on the subreddit name
86+
* * `SubredditState`
87+
*
88+
* EX `["mealtimevideos","askscience", "/onlyfans*\/i", {"over18": true}]`
89+
* @examples [["mealtimevideos","askscience", "/onlyfans*\/i", {"over18": true}]]
8590
* */
8691
include?: string[],
8792
/**
88-
* Do not include Activities from this list of Subreddits (by name, case-insensitive)
93+
* When present, Activities WILL NOT be counted if they are found in this list of Subreddits
8994
*
90-
* Will be ignored if `include` is present.
95+
* Each value in the list can be either:
9196
*
92-
* EX `["mealtimevideos","askscience"]`
93-
* @examples ["mealtimevideos","askscience"]
94-
* @minItems 1
97+
* * string (name of subreddit)
98+
* * regular expression to run on the subreddit name
99+
* * `SubredditState`
100+
*
101+
* EX `["mealtimevideos","askscience", "/onlyfans*\/i", {"over18": true}]`
102+
* @examples [["mealtimevideos","askscience", "/onlyfans*\/i", {"over18": true}]]
95103
* */
96104
exclude?: string[],
97105

106+
/**
107+
* When present, Submissions from `window` will only be counted if they meet this criteria
108+
* */
109+
submissionState?: SubmissionState
110+
/**
111+
* When present, Comments from `window` will only be counted if they meet this criteria
112+
* */
113+
commentState?: CommentState
114+
98115
/**
99116
* This list determines which categories of domains should be aggregated on. All aggregated domains will be tested against `threshold`
100117
*
@@ -178,21 +195,36 @@ export class AttributionRule extends Rule {
178195
consolidateMediaDomains = false,
179196
domains = [],
180197
domainsCombined = false,
181-
include: includeRaw = [],
182-
exclude: excludeRaw = [],
198+
include = [],
199+
exclude = [],
200+
commentState,
201+
submissionState,
183202
} = criteria;
184203

185-
const include = includeRaw.map(x => parseSubredditName(x).toLowerCase());
186-
const exclude = excludeRaw.map(x => parseSubredditName(x).toLowerCase());
187-
188204
const {operator, value, isPercent, extra = ''} = parseGenericValueOrPercentComparison(threshold);
189205

190206
let activities = thresholdOn === 'submissions' ? await this.resources.getAuthorSubmissions(item.author, {window: window}) : await this.resources.getAuthorActivities(item.author, {window: window});
191-
activities = activities.filter(act => {
192-
if (include.length > 0) {
193-
return include.some(x => x === getActivitySubredditName(act).toLowerCase());
194-
} else if (exclude.length > 0) {
195-
return !exclude.some(x => x === getActivitySubredditName(act).toLowerCase())
207+
208+
if(include.length > 0 || exclude.length > 0) {
209+
const defaultOpts = {
210+
defaultFlags: 'i',
211+
generateDescription: true
212+
};
213+
if(include.length > 0) {
214+
const subStates = include.map(x => convertSubredditsRawToStrong(x, defaultOpts));
215+
activities = await this.resources.batchTestSubredditCriteria(activities, subStates);
216+
} else {
217+
const subStates = exclude.map(x => convertSubredditsRawToStrong(x, defaultOpts));
218+
const toExclude = (await this.resources.batchTestSubredditCriteria(activities, subStates)).map(x => x.id);
219+
activities = activities.filter(x => !toExclude.includes(x.id));
220+
}
221+
}
222+
223+
activities = await as.filter(activities, async (activity) => {
224+
if (asSubmission(activity) && submissionState !== undefined) {
225+
return await this.resources.testItemCriteria(activity, [submissionState]);
226+
} else if (commentState !== undefined) {
227+
return await this.resources.testItemCriteria(activity, [commentState]);
196228
}
197229
return true;
198230
});

src/Rule/RecentActivityRule.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
asSubmission, bitsToHexLength,
1111
// blockHashImage,
1212
compareImages,
13-
comparisonTextOp,
13+
comparisonTextOp, convertSubredditsRawToStrong,
1414
FAIL,
1515
formatNumber,
1616
getActivitySubredditName, imageCompareMaxConcurrencyGuess,
@@ -284,15 +284,11 @@ export class RecentActivityRule extends Rule {
284284
} = triggerSet;
285285

286286
// convert subreddits array into entirely StrongSubredditState
287-
const subStates: StrongSubredditState[] = subreddits.map((x) => {
288-
if (typeof x === 'string') {
289-
return toStrongSubredditState({name: x, stateDescription: x}, {
290-
defaultFlags: 'i',
291-
generateDescription: true
292-
});
293-
}
294-
return toStrongSubredditState(x, {defaultFlags: 'i', generateDescription: true});
295-
});
287+
const defaultOpts = {
288+
defaultFlags: 'i',
289+
generateDescription: true
290+
};
291+
const subStates: StrongSubredditState[] = subreddits.map((x) => convertSubredditsRawToStrong(x, defaultOpts));
296292

297293
let validActivity: (Comment | Submission)[] = await as.filter(viableActivity, async (activity) => {
298294
if (asSubmission(activity) && submissionState !== undefined) {

src/Schema/Action.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@
2929
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
3030
"type": "string"
3131
},
32+
"description": {
33+
"anyOf": [
34+
{
35+
"items": {
36+
"type": "string"
37+
},
38+
"type": "array"
39+
},
40+
{
41+
"type": "string"
42+
}
43+
],
44+
"description": "An (array of) string/regular expression to test contents of an Author's profile description against\n\nIf no flags are specified then the **insensitive** flag is used by default\n\nIf using an array then if **any** value in the array passes the description test passes",
45+
"examples": [
46+
[
47+
"/test$/i",
48+
"look for this string literal"
49+
]
50+
]
51+
},
3252
"flairCssClass": {
3353
"description": "A list of (user) flair css class values from the subreddit to match against",
3454
"examples": [

src/Schema/App.json

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,16 @@
249249
},
250250
"type": "array"
251251
},
252+
"commentState": {
253+
"$ref": "#/definitions/CommentState",
254+
"description": "When present, Comments from `window` will only be counted if they meet this criteria",
255+
"examples": [
256+
{
257+
"op": true,
258+
"removed": false
259+
}
260+
]
261+
},
252262
"consolidateMediaDomains": {
253263
"default": false,
254264
"description": "Should the criteria consolidate recognized media domains into the parent domain?\n\nSubmissions to major media domains (youtube, vimeo) can be identified by individual Channel/Author...\n\n* If `false` then domains will be aggregated at the channel level IE Youtube Channel A (2 counts), Youtube Channel B (3 counts)\n* If `true` then then media domains will be consolidated at domain level and then aggregated IE youtube.com (5 counts)",
@@ -277,27 +287,37 @@
277287
"type": "boolean"
278288
},
279289
"exclude": {
280-
"description": "Do not include Activities from this list of Subreddits (by name, case-insensitive)\n\nWill be ignored if `include` is present.\n\nEX `[\"mealtimevideos\",\"askscience\"]`",
290+
"description": "When present, Activities WILL NOT be counted if they are found in this list of Subreddits\n\nEach value in the list can be either:\n\n * string (name of subreddit)\n * regular expression to run on the subreddit name\n * `SubredditState`\n\nEX `[\"mealtimevideos\",\"askscience\", \"/onlyfans*\\/i\", {\"over18\": true}]`",
281291
"examples": [
282-
"mealtimevideos",
283-
"askscience"
292+
[
293+
"mealtimevideos",
294+
"askscience",
295+
"/onlyfans*/i",
296+
{
297+
"over18": true
298+
}
299+
]
284300
],
285301
"items": {
286302
"type": "string"
287303
},
288-
"minItems": 1,
289304
"type": "array"
290305
},
291306
"include": {
292-
"description": "Only include Activities from this list of Subreddits (by name, case-insensitive)\n\n\nEX `[\"mealtimevideos\",\"askscience\"]`",
307+
"description": "When present, Activities WILL ONLY be counted if they are found in this list of Subreddits\n\nEach value in the list can be either:\n\n * string (name of subreddit)\n * regular expression to run on the subreddit name\n * `SubredditState`\n\nEX `[\"mealtimevideos\",\"askscience\", \"/onlyfans*\\/i\", {\"over18\": true}]`",
293308
"examples": [
294-
"mealtimevideos",
295-
"askscience"
309+
[
310+
"mealtimevideos",
311+
"askscience",
312+
"/onlyfans*/i",
313+
{
314+
"over18": true
315+
}
316+
]
296317
],
297318
"items": {
298319
"type": "string"
299320
},
300-
"minItems": 1,
301321
"type": "array"
302322
},
303323
"minActivityCount": {
@@ -308,6 +328,16 @@
308328
"name": {
309329
"type": "string"
310330
},
331+
"submissionState": {
332+
"$ref": "#/definitions/SubmissionState",
333+
"description": "When present, Submissions from `window` will only be counted if they meet this criteria",
334+
"examples": [
335+
{
336+
"over_18": true,
337+
"removed": false
338+
}
339+
]
340+
},
311341
"threshold": {
312342
"default": "> 10%",
313343
"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",
@@ -454,6 +484,26 @@
454484
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(%?)(.*)$",
455485
"type": "string"
456486
},
487+
"description": {
488+
"anyOf": [
489+
{
490+
"items": {
491+
"type": "string"
492+
},
493+
"type": "array"
494+
},
495+
{
496+
"type": "string"
497+
}
498+
],
499+
"description": "An (array of) string/regular expression to test contents of an Author's profile description against\n\nIf no flags are specified then the **insensitive** flag is used by default\n\nIf using an array then if **any** value in the array passes the description test passes",
500+
"examples": [
501+
[
502+
"/test$/i",
503+
"look for this string literal"
504+
]
505+
]
506+
},
457507
"flairCssClass": {
458508
"description": "A list of (user) flair css class values from the subreddit to match against",
459509
"examples": [

0 commit comments

Comments
 (0)