@fsvreddit/bot-bouncer-evaluation 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +1 -0
  2. package/dist/allEvaluators.d.ts +16 -0
  3. package/dist/allEvaluators.js +72 -0
  4. package/dist/constants.d.ts +3 -0
  5. package/dist/constants.js +6 -0
  6. package/dist/extendedDevvit.d.ts +36 -0
  7. package/dist/extendedDevvit.js +66 -0
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.js +23 -0
  10. package/dist/userEvaluation/EvaluateBadDisplayname.d.ts +18 -0
  11. package/dist/userEvaluation/EvaluateBadDisplayname.js +81 -0
  12. package/dist/userEvaluation/EvaluateBadDisplaynameDefinedHandles.d.ts +16 -0
  13. package/dist/userEvaluation/EvaluateBadDisplaynameDefinedHandles.js +89 -0
  14. package/dist/userEvaluation/EvaluateBadUsername.d.ts +16 -0
  15. package/dist/userEvaluation/EvaluateBadUsername.js +75 -0
  16. package/dist/userEvaluation/EvaluateBioText.d.ts +18 -0
  17. package/dist/userEvaluation/EvaluateBioText.js +96 -0
  18. package/dist/userEvaluation/EvaluateBioTextDefinedHandles.d.ts +16 -0
  19. package/dist/userEvaluation/EvaluateBioTextDefinedHandles.js +99 -0
  20. package/dist/userEvaluation/EvaluateBotGroupAdvanced.d.ts +124 -0
  21. package/dist/userEvaluation/EvaluateBotGroupAdvanced.js +1360 -0
  22. package/dist/userEvaluation/EvaluateBotGroupAdvancedInternal.d.ts +6 -0
  23. package/dist/userEvaluation/EvaluateBotGroupAdvancedInternal.js +13 -0
  24. package/dist/userEvaluation/EvaluateBotGroupSubmitters.d.ts +8 -0
  25. package/dist/userEvaluation/EvaluateBotGroupSubmitters.js +23 -0
  26. package/dist/userEvaluation/EvaluateCommentPhrase.d.ts +16 -0
  27. package/dist/userEvaluation/EvaluateCommentPhrase.js +86 -0
  28. package/dist/userEvaluation/EvaluateDomainSharer.d.ts +18 -0
  29. package/dist/userEvaluation/EvaluateDomainSharer.js +138 -0
  30. package/dist/userEvaluation/EvaluateFirstCommentEmDash.d.ts +17 -0
  31. package/dist/userEvaluation/EvaluateFirstCommentEmDash.js +103 -0
  32. package/dist/userEvaluation/EvaluateFirstPostWithSelfComment.d.ts +14 -0
  33. package/dist/userEvaluation/EvaluateFirstPostWithSelfComment.js +53 -0
  34. package/dist/userEvaluation/EvaluateInconsistentAgeBot.d.ts +17 -0
  35. package/dist/userEvaluation/EvaluateInconsistentAgeBot.js +91 -0
  36. package/dist/userEvaluation/EvaluateInconsistentGenderBot.d.ts +17 -0
  37. package/dist/userEvaluation/EvaluateInconsistentGenderBot.js +99 -0
  38. package/dist/userEvaluation/EvaluateLinkReuse.d.ts +17 -0
  39. package/dist/userEvaluation/EvaluateLinkReuse.js +159 -0
  40. package/dist/userEvaluation/EvaluateMixedBot.d.ts +15 -0
  41. package/dist/userEvaluation/EvaluateMixedBot.js +82 -0
  42. package/dist/userEvaluation/EvaluateObfuscatedBioKeywords.d.ts +16 -0
  43. package/dist/userEvaluation/EvaluateObfuscatedBioKeywords.js +79 -0
  44. package/dist/userEvaluation/EvaluatePinnedPostTitle.d.ts +15 -0
  45. package/dist/userEvaluation/EvaluatePinnedPostTitle.js +77 -0
  46. package/dist/userEvaluation/EvaluatePostTitle.d.ts +16 -0
  47. package/dist/userEvaluation/EvaluatePostTitle.js +91 -0
  48. package/dist/userEvaluation/EvaluatePostTitleDefinedHandles.d.ts +15 -0
  49. package/dist/userEvaluation/EvaluatePostTitleDefinedHandles.js +109 -0
  50. package/dist/userEvaluation/EvaluatePostTitleMulti.d.ts +16 -0
  51. package/dist/userEvaluation/EvaluatePostTitleMulti.js +88 -0
  52. package/dist/userEvaluation/EvaluateRapidFireBot.d.ts +13 -0
  53. package/dist/userEvaluation/EvaluateRapidFireBot.js +48 -0
  54. package/dist/userEvaluation/EvaluateRepostBot.d.ts +19 -0
  55. package/dist/userEvaluation/EvaluateRepostBot.js +167 -0
  56. package/dist/userEvaluation/EvaluateSelfComment.d.ts +16 -0
  57. package/dist/userEvaluation/EvaluateSelfComment.js +74 -0
  58. package/dist/userEvaluation/EvaluateSocialLinks.d.ts +16 -0
  59. package/dist/userEvaluation/EvaluateSocialLinks.js +101 -0
  60. package/dist/userEvaluation/EvaluateSuspiciousFirstPost.d.ts +15 -0
  61. package/dist/userEvaluation/EvaluateSuspiciousFirstPost.js +68 -0
  62. package/dist/userEvaluation/EvaluateTGGroup.d.ts +16 -0
  63. package/dist/userEvaluation/EvaluateTGGroup.js +73 -0
  64. package/dist/userEvaluation/EvaluateTextInNsfwImages.d.ts +14 -0
  65. package/dist/userEvaluation/EvaluateTextInNsfwImages.js +169 -0
  66. package/dist/userEvaluation/EvaluateTitleCopyBot.d.ts +13 -0
  67. package/dist/userEvaluation/EvaluateTitleCopyBot.js +74 -0
  68. package/dist/userEvaluation/EvaluateWarmupBot.d.ts +15 -0
  69. package/dist/userEvaluation/EvaluateWarmupBot.js +89 -0
  70. package/dist/userEvaluation/EvaluateWorldTraveller.d.ts +18 -0
  71. package/dist/userEvaluation/EvaluateWorldTraveller.js +79 -0
  72. package/dist/userEvaluation/UserEvaluatorBase.d.ts +67 -0
  73. package/dist/userEvaluation/UserEvaluatorBase.js +109 -0
  74. package/dist/userEvaluation/evaluatorHelpers.d.ts +3 -0
  75. package/dist/userEvaluation/evaluatorHelpers.js +25 -0
  76. package/dist/userEvaluation/index.d.ts +26 -0
  77. package/dist/userEvaluation/index.js +42 -0
  78. package/dist/utility.d.ts +34 -0
  79. package/dist/utility.js +164 -0
  80. package/eslint.config.mjs +3 -0
  81. package/package.json +50 -0
  82. package/tsconfig.build.json +14 -0
  83. package/tsconfig.json +23 -0
@@ -0,0 +1,6 @@
1
+ import { EvaluateBotGroupAdvanced } from "./EvaluateBotGroupAdvanced";
2
+ export declare class EvaluateBotGroupAdvancedInternal extends EvaluateBotGroupAdvanced {
3
+ name: string;
4
+ shortname: string;
5
+ allowNewFeatures: boolean;
6
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateBotGroupAdvancedInternal = void 0;
4
+ const EvaluateBotGroupAdvanced_1 = require("./EvaluateBotGroupAdvanced");
5
+ class EvaluateBotGroupAdvancedInternal extends EvaluateBotGroupAdvanced_1.EvaluateBotGroupAdvanced {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.name = "Bot Group Advanced Internal";
9
+ this.shortname = "botgroupadvancedinternal";
10
+ this.allowNewFeatures = true;
11
+ }
12
+ }
13
+ exports.EvaluateBotGroupAdvancedInternal = EvaluateBotGroupAdvancedInternal;
@@ -0,0 +1,8 @@
1
+ import { EvaluateBotGroupAdvanced } from "./EvaluateBotGroupAdvanced";
2
+ import { ValidationIssue } from "./UserEvaluatorBase";
3
+ export declare class EvaluateBotGroupAdvancedSubmitters extends EvaluateBotGroupAdvanced {
4
+ name: string;
5
+ shortname: string;
6
+ allowNewFeatures: boolean;
7
+ validateVariables(): ValidationIssue[];
8
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateBotGroupAdvancedSubmitters = void 0;
4
+ const constants_1 = require("../constants");
5
+ const EvaluateBotGroupAdvanced_1 = require("./EvaluateBotGroupAdvanced");
6
+ class EvaluateBotGroupAdvancedSubmitters extends EvaluateBotGroupAdvanced_1.EvaluateBotGroupAdvanced {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.name = "Bot Group Advanced (Submitters)";
10
+ this.shortname = constants_1.SUBMITTER_BOT_NAME;
11
+ this.allowNewFeatures = true;
12
+ }
13
+ validateVariables() {
14
+ const issues = super.validateVariables();
15
+ for (const group of this.getBotGroups()) {
16
+ if (group.submitterName === undefined) {
17
+ issues.push({ severity: "error", message: "submitterName is required for all bot groups in this evaluator." });
18
+ }
19
+ }
20
+ return issues;
21
+ }
22
+ }
23
+ exports.EvaluateBotGroupAdvancedSubmitters = EvaluateBotGroupAdvancedSubmitters;
@@ -0,0 +1,16 @@
1
+ import { Post } from "@devvit/public-api";
2
+ import { CommentCreate } from "@devvit/protos";
3
+ import { EvaluatorRegex, UserEvaluatorBase, ValidationIssue } from "./UserEvaluatorBase.js";
4
+ import { UserExtended } from "@fsvreddit/fsv-devvit-helpers";
5
+ export declare class EvaluateCommentPhrase extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ banContentThreshold: number;
9
+ validateVariables(): ValidationIssue[];
10
+ gatherRegexes(): EvaluatorRegex[];
11
+ private eligibleComment;
12
+ preEvaluateComment(event: CommentCreate): boolean;
13
+ preEvaluatePost(_: Post): boolean;
14
+ preEvaluateUser(user: UserExtended): boolean;
15
+ evaluate(_: UserExtended): boolean;
16
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateCommentPhrase = void 0;
4
+ const UserEvaluatorBase_js_1 = require("./UserEvaluatorBase.js");
5
+ const date_fns_1 = require("date-fns");
6
+ const lodash_1 = require("lodash");
7
+ class EvaluateCommentPhrase extends UserEvaluatorBase_js_1.UserEvaluatorBase {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.name = "Comment Phrase";
11
+ this.shortname = "commentphrase";
12
+ this.banContentThreshold = 1;
13
+ }
14
+ validateVariables() {
15
+ const results = [];
16
+ const regexes = this.getVariable("phrases", []);
17
+ for (const regexVal of regexes) {
18
+ let regex;
19
+ try {
20
+ regex = new RegExp(regexVal);
21
+ }
22
+ catch {
23
+ results.push({ severity: "error", message: `Invalid regex in biotext: ${regexVal}` });
24
+ continue;
25
+ }
26
+ if (regex.test("")) {
27
+ results.push({ severity: "error", message: `Bio Text regex is too greedy: ${regexVal}` });
28
+ }
29
+ }
30
+ return results;
31
+ }
32
+ gatherRegexes() {
33
+ const phrases = this.getVariable("phrases", []);
34
+ return (0, lodash_1.uniq)(phrases.map(phrase => ({
35
+ evaluatorName: this.name,
36
+ subName: this.shortname,
37
+ regex: phrase,
38
+ })));
39
+ }
40
+ eligibleComment(comment) {
41
+ const phrases = this.getVariable("phrases", []);
42
+ const maxCommentAgeInDays = this.getVariable("maxcommentageindays", 30);
43
+ if (phrases.length === 0) {
44
+ return false;
45
+ }
46
+ return comment.createdAt > (0, date_fns_1.subDays)(new Date(), maxCommentAgeInDays)
47
+ && phrases.some(phrase => new RegExp(phrase).test(comment.body));
48
+ }
49
+ preEvaluateComment(event) {
50
+ if (!event.comment) {
51
+ return false;
52
+ }
53
+ return this.eligibleComment(event.comment);
54
+ }
55
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
56
+ preEvaluatePost(_) {
57
+ return false;
58
+ }
59
+ preEvaluateUser(user) {
60
+ const maxAgeInDays = this.getVariable("maxageindays", 60);
61
+ const maxCommentKarma = this.getVariable("maxcommentkarma", 100);
62
+ return user.commentKarma < maxCommentKarma && user.createdAt > (0, date_fns_1.subDays)(new Date(), maxAgeInDays);
63
+ }
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
+ evaluate(_) {
66
+ const userComments = this.getComments();
67
+ const matchingComments = userComments.filter(comment => this.eligibleComment(comment));
68
+ if (matchingComments.length === 0) {
69
+ return false;
70
+ }
71
+ const minNumberOfMatchingComments = this.getVariable("minnumberofmatchingcomments", 1);
72
+ if (matchingComments.length < minNumberOfMatchingComments) {
73
+ return false;
74
+ }
75
+ const matchingComment = matchingComments[0];
76
+ const phrases = this.getVariable("phrases", []);
77
+ const matchedPhrase = phrases.find(phrase => new RegExp(phrase).test(matchingComment.body));
78
+ if (!matchedPhrase) {
79
+ // Impossible to reach this point.
80
+ return false;
81
+ }
82
+ this.addHitReason(`Comment found matching regex: "${matchedPhrase}"`);
83
+ return true;
84
+ }
85
+ }
86
+ exports.EvaluateCommentPhrase = EvaluateCommentPhrase;
@@ -0,0 +1,18 @@
1
+ import { Post } from "@devvit/public-api";
2
+ import { CommentCreate } from "@devvit/protos";
3
+ import { EvaluatorRegex, UserEvaluatorBase } from "./UserEvaluatorBase.js";
4
+ import { UserExtended } from "@fsvreddit/fsv-devvit-helpers";
5
+ export declare class EvaluateDomainSharer extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ canAutoBan: boolean;
9
+ gatherRegexes(): EvaluatorRegex[];
10
+ private domainsFromContent;
11
+ private ignoredSubreddits;
12
+ private ignoredSubredditRegexes;
13
+ private domainsFromPost;
14
+ preEvaluateComment(event: CommentCreate): boolean;
15
+ preEvaluatePost(post: Post): boolean;
16
+ preEvaluateUser(user: UserExtended): boolean;
17
+ evaluate(_: UserExtended): boolean;
18
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateDomainSharer = void 0;
4
+ const UserEvaluatorBase_js_1 = require("./UserEvaluatorBase.js");
5
+ const lodash_1 = require("lodash");
6
+ const date_fns_1 = require("date-fns");
7
+ const evaluatorHelpers_js_1 = require("./evaluatorHelpers.js");
8
+ class EvaluateDomainSharer extends UserEvaluatorBase_js_1.UserEvaluatorBase {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.name = "Domain Sharer";
12
+ this.shortname = "domainsharer";
13
+ this.canAutoBan = false;
14
+ }
15
+ gatherRegexes() {
16
+ const ignoredSubredditRegexes = this.getVariable("ignoredSubredditRegexes", []);
17
+ return ignoredSubredditRegexes.map(regex => ({
18
+ evaluatorName: this.name,
19
+ regex,
20
+ }));
21
+ }
22
+ domainsFromContent(content) {
23
+ // eslint-disable-next-line no-useless-escape
24
+ const domainRegex = /(https?:\/\/[\w\.]+)[\/\)]/g;
25
+ const matches = content.matchAll(domainRegex);
26
+ const domains = [];
27
+ for (const match of matches) {
28
+ const [, url] = match;
29
+ domains.push((0, evaluatorHelpers_js_1.domainFromUrl)(url));
30
+ }
31
+ const redditDomains = this.getGenericVariable("redditdomains", []);
32
+ const ignoredDomains = this.getVariable("ignoreddomains", []);
33
+ const ignoredDomainSuffixes = this.getVariable("ignoredDomainSuffixes", []);
34
+ return (0, lodash_1.uniq)((0, lodash_1.compact)((domains)).filter((domain) => {
35
+ if (redditDomains.includes(domain)) {
36
+ return false;
37
+ }
38
+ if (ignoredDomains.includes(domain)) {
39
+ return false;
40
+ }
41
+ if (ignoredDomainSuffixes.some(suffix => domain.endsWith(suffix))) {
42
+ return false;
43
+ }
44
+ return true;
45
+ }));
46
+ }
47
+ ignoredSubreddits() {
48
+ return this.getVariable("ignoredsubreddits", []);
49
+ }
50
+ ignoredSubredditRegexes() {
51
+ const regexStrings = this.getVariable("ignoredSubredditRegexes", []);
52
+ return regexStrings.map(str => new RegExp(str));
53
+ }
54
+ domainsFromPost(post) {
55
+ const domains = [];
56
+ if (!post.url.startsWith("/")) {
57
+ domains.push((0, evaluatorHelpers_js_1.domainFromUrl)(post.url));
58
+ }
59
+ if (post.body) {
60
+ domains.push(...this.domainsFromContent(post.body));
61
+ }
62
+ const redditDomains = this.getGenericVariable("redditdomains", []);
63
+ const ignoredDomains = this.getVariable("ignoreddomains", []);
64
+ const ignoredDomainSuffixes = this.getVariable("ignoredDomainSuffixes", []);
65
+ return (0, lodash_1.uniq)((0, lodash_1.compact)((domains)).filter((domain) => {
66
+ if (redditDomains.includes(domain)) {
67
+ return false;
68
+ }
69
+ if (ignoredDomains.includes(domain)) {
70
+ return false;
71
+ }
72
+ if (ignoredDomainSuffixes.some(suffix => domain.endsWith(suffix))) {
73
+ return false;
74
+ }
75
+ return true;
76
+ }));
77
+ }
78
+ preEvaluateComment(event) {
79
+ if (!event.comment) {
80
+ return false;
81
+ }
82
+ if (event.subreddit?.name && this.ignoredSubreddits().includes(event.subreddit.name)) {
83
+ return false;
84
+ }
85
+ if (event.subreddit?.name && this.ignoredSubredditRegexes().some(regex => regex.test(event.subreddit?.name ?? ""))) {
86
+ return false;
87
+ }
88
+ return this.domainsFromContent(event.comment.body).length > 0;
89
+ }
90
+ preEvaluatePost(post) {
91
+ if (this.ignoredSubreddits().includes(post.subredditName)) {
92
+ return false;
93
+ }
94
+ if (this.ignoredSubredditRegexes().some(regex => regex.test(post.subredditName))) {
95
+ return false;
96
+ }
97
+ return this.domainsFromPost(post).length > 0;
98
+ }
99
+ preEvaluateUser(user) {
100
+ return user.commentKarma < 1000 && user.linkKarma < 1000;
101
+ }
102
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
103
+ evaluate(_) {
104
+ const recentPosts = this.getPosts({ since: (0, date_fns_1.subMonths)(new Date(), 6) })
105
+ .filter(post => !this.ignoredSubreddits().includes(post.subredditName) && !this.ignoredSubredditRegexes().some(regex => regex.test(post.subredditName)));
106
+ const recentComments = this.getComments({ since: (0, date_fns_1.subMonths)(new Date(), 6) })
107
+ .filter(comment => !this.ignoredSubreddits().includes(comment.subredditName) && !this.ignoredSubredditRegexes().some(regex => regex.test(comment.subredditName)));
108
+ const recentContent = [...recentPosts, ...recentComments];
109
+ if (recentContent.length < 5) {
110
+ return false;
111
+ }
112
+ const domains = [];
113
+ for (const post of recentPosts) {
114
+ domains.push(...this.domainsFromPost(post));
115
+ }
116
+ for (const comment of recentComments) {
117
+ domains.push(...this.domainsFromContent(comment.body));
118
+ }
119
+ if (domains.length === 0) {
120
+ return false;
121
+ }
122
+ const domainAggregate = (0, lodash_1.toPairs)((0, lodash_1.countBy)(domains)).map(([domain, count]) => ({ domain, count }));
123
+ const dominantDomains = domainAggregate.filter(item => item.count === recentContent.length);
124
+ if (dominantDomains.length > 0) {
125
+ const autobanDomains = this.getVariable("autobandomains", []);
126
+ if (autobanDomains.some(domain => dominantDomains.some(item => item.domain === domain))) {
127
+ this.canAutoBan = true;
128
+ this.banContentThreshold = 5;
129
+ }
130
+ this.addHitReason(`User has shared ${recentContent.length} posts with the same domain: ${dominantDomains.map(item => item.domain).join(", ")}`);
131
+ return true;
132
+ }
133
+ else {
134
+ return false;
135
+ }
136
+ }
137
+ }
138
+ exports.EvaluateDomainSharer = EvaluateDomainSharer;
@@ -0,0 +1,17 @@
1
+ import { Comment, Post } from "@devvit/public-api";
2
+ import { CommentCreate } from "@devvit/protos";
3
+ import { UserEvaluatorBase } from "./UserEvaluatorBase.js";
4
+ import { UserExtended } from "../extendedDevvit.js";
5
+ export declare class EvaluateFirstCommentEmDash extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ banContentThreshold: number;
9
+ private readonly emDashRegex;
10
+ private isNoCheckSub;
11
+ private eligibleComment;
12
+ preEvaluateComment(event: CommentCreate): boolean;
13
+ private eligiblePost;
14
+ preEvaluatePost(post: Post): boolean;
15
+ preEvaluateUser(user: UserExtended): boolean;
16
+ evaluate(_: UserExtended, history: (Post | Comment)[]): boolean;
17
+ }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateFirstCommentEmDash = void 0;
4
+ const UserEvaluatorBase_js_1 = require("./UserEvaluatorBase.js");
5
+ const tid_js_1 = require("@devvit/public-api/types/tid.js");
6
+ const date_fns_1 = require("date-fns");
7
+ const lodash_1 = require("lodash");
8
+ const evaluatorHelpers_js_1 = require("./evaluatorHelpers.js");
9
+ class EvaluateFirstCommentEmDash extends UserEvaluatorBase_js_1.UserEvaluatorBase {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.name = "First Comment Em Dash";
13
+ this.shortname = "em-dash";
14
+ this.banContentThreshold = 1;
15
+ this.emDashRegex = /\w—\w/i;
16
+ }
17
+ isNoCheckSub() {
18
+ const noCheckSubs = this.getVariable("nochecksubs", []);
19
+ return this.context.subredditName && noCheckSubs.includes(this.context.subredditName);
20
+ }
21
+ eligibleComment(comment) {
22
+ return (0, tid_js_1.isLinkId)(comment.parentId);
23
+ }
24
+ preEvaluateComment(event) {
25
+ if (!event.comment) {
26
+ return false;
27
+ }
28
+ if (this.isNoCheckSub()) {
29
+ return false;
30
+ }
31
+ return this.eligibleComment(event.comment);
32
+ }
33
+ eligiblePost(post) {
34
+ const domain = (0, evaluatorHelpers_js_1.domainFromUrl)(post.url);
35
+ const redditDomains = this.getGenericVariable("redditdomains", []);
36
+ return domain !== undefined && redditDomains.includes(domain);
37
+ }
38
+ preEvaluatePost(post) {
39
+ if (this.isNoCheckSub()) {
40
+ return false;
41
+ }
42
+ return this.eligiblePost(post);
43
+ }
44
+ preEvaluateUser(user) {
45
+ if (user.createdAt < (0, date_fns_1.subMonths)(new Date(), 2)) {
46
+ return false;
47
+ }
48
+ if (this.isNoCheckSub()) {
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ evaluate(_, history) {
54
+ const comments = this.getComments(history);
55
+ const posts = this.getPosts(history);
56
+ if (comments.length > 30) {
57
+ this.canAutoBan = false;
58
+ return false;
59
+ }
60
+ if (comments.length === 0) {
61
+ return false;
62
+ }
63
+ if (comments.some(comment => !this.eligibleComment(comment))) {
64
+ return false;
65
+ }
66
+ if (comments.some(comment => posts.some(post => post.id === comment.parentId))) {
67
+ return false;
68
+ }
69
+ const firstComment = (0, lodash_1.last)(comments);
70
+ const firstCommentContainsEmDash = firstComment ? this.emDashRegex.test(firstComment.body) : false;
71
+ let emDashThreshold;
72
+ if (comments.length > 80) {
73
+ emDashThreshold = 0.2;
74
+ }
75
+ else if (comments.length > 30) {
76
+ emDashThreshold = 0.25;
77
+ }
78
+ else {
79
+ emDashThreshold = 0.3;
80
+ }
81
+ const emDashThresholdMet = comments.filter(comment => this.emDashRegex.test(comment.body)).length / comments.length > emDashThreshold;
82
+ if (!firstCommentContainsEmDash && !emDashThresholdMet) {
83
+ const karmaFarmingSubs = this.getGenericVariable("karmafarminglinksubs", []);
84
+ const postCountNeeded = this.getVariable("postcount", 3);
85
+ const subsNeeded = this.getVariable("distinctsubs", 3);
86
+ const backupRequirementsMet = posts.length >= postCountNeeded
87
+ && (0, lodash_1.uniq)(posts.filter(post => karmaFarmingSubs.includes(post.subredditName)).map(post => post.subredditName)).length >= subsNeeded
88
+ && comments.filter(comment => comment.body.includes("—")).length > 1;
89
+ if (!backupRequirementsMet) {
90
+ return false;
91
+ }
92
+ }
93
+ if (posts.length > 0 && posts.some(post => !this.eligiblePost(post))) {
94
+ return false;
95
+ }
96
+ const noAutoBanSubs = this.getVariable("noautobansubs", []);
97
+ if (history.some(item => noAutoBanSubs.includes(item.subredditName))) {
98
+ this.canAutoBan = false;
99
+ }
100
+ return true;
101
+ }
102
+ }
103
+ exports.EvaluateFirstCommentEmDash = EvaluateFirstCommentEmDash;
@@ -0,0 +1,14 @@
1
+ import { Comment, Post } from "@devvit/public-api";
2
+ import { CommentCreate } from "@devvit/protos";
3
+ import { UserEvaluatorBase } from "./UserEvaluatorBase.js";
4
+ import { UserExtended } from "../extendedDevvit.js";
5
+ export declare class EvaluateFirstPostWithSelfComment extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ canAutoBan: boolean;
9
+ banContentThreshold: number;
10
+ preEvaluateComment(event: CommentCreate): boolean;
11
+ preEvaluatePost(post: Post): boolean;
12
+ preEvaluateUser(user: UserExtended): boolean;
13
+ evaluate(user: UserExtended, history: (Post | Comment)[]): boolean;
14
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateFirstPostWithSelfComment = void 0;
4
+ const UserEvaluatorBase_js_1 = require("./UserEvaluatorBase.js");
5
+ const date_fns_1 = require("date-fns");
6
+ class EvaluateFirstPostWithSelfComment extends UserEvaluatorBase_js_1.UserEvaluatorBase {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.name = "First Post with Self Comment Bot";
10
+ this.shortname = "firstpostselfcomment";
11
+ this.canAutoBan = true;
12
+ this.banContentThreshold = 1;
13
+ }
14
+ preEvaluateComment(event) {
15
+ if (!event.author?.id) {
16
+ return false;
17
+ }
18
+ return event.author.id === event.post?.authorId;
19
+ }
20
+ preEvaluatePost(post) {
21
+ return post.body !== undefined && post.body.length > 0;
22
+ }
23
+ preEvaluateUser(user) {
24
+ const maxCommentKarma = this.getVariable("maxcommentkarma", 1);
25
+ const maxLinkKarma = this.getVariable("maxlinkkarma", 1);
26
+ return user.commentKarma < maxCommentKarma
27
+ && user.linkKarma < maxLinkKarma;
28
+ }
29
+ evaluate(user, history) {
30
+ if (history.length >= 50) {
31
+ return false;
32
+ }
33
+ const oldestPost = this.getPosts(history).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).shift();
34
+ if (!oldestPost) {
35
+ return false;
36
+ }
37
+ if (oldestPost.subredditName !== `u_${user.username}`) {
38
+ return false;
39
+ }
40
+ if (oldestPost.createdAt < (0, date_fns_1.subDays)(new Date(), 7)) {
41
+ return false;
42
+ }
43
+ if (oldestPost.numberOfComments > 1) {
44
+ return false;
45
+ }
46
+ const comments = this.getComments(history);
47
+ if (!comments.some(comment => comment.postId === oldestPost.id && comment.authorId === oldestPost.authorId)) {
48
+ return false;
49
+ }
50
+ return true;
51
+ }
52
+ }
53
+ exports.EvaluateFirstPostWithSelfComment = EvaluateFirstPostWithSelfComment;
@@ -0,0 +1,17 @@
1
+ import { CommentCreate } from "@devvit/protos";
2
+ import { EvaluatorRegex, UserEvaluatorBase, ValidationIssue } from "./UserEvaluatorBase.js";
3
+ import { Post } from "@devvit/public-api";
4
+ import { UserExtended } from "@fsvreddit/fsv-devvit-helpers";
5
+ export declare class EvaluateInconsistentAgeBot extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ banContentThreshold: number;
9
+ canAutoBan: boolean;
10
+ gatherRegexes(): EvaluatorRegex[];
11
+ validateVariables(): ValidationIssue[];
12
+ preEvaluateComment(_event: CommentCreate): boolean;
13
+ private getAgeFromPostTitle;
14
+ preEvaluatePost(post: Post): boolean;
15
+ preEvaluateUser(user: UserExtended): boolean;
16
+ evaluate(user: UserExtended): boolean;
17
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvaluateInconsistentAgeBot = void 0;
4
+ const UserEvaluatorBase_js_1 = require("./UserEvaluatorBase.js");
5
+ const lodash_1 = require("lodash");
6
+ const date_fns_1 = require("date-fns");
7
+ const regjsparser_1 = require("regjsparser");
8
+ class EvaluateInconsistentAgeBot extends UserEvaluatorBase_js_1.UserEvaluatorBase {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.name = "Inconsistent Age Bot";
12
+ this.shortname = "inconsistentage";
13
+ this.banContentThreshold = 6;
14
+ this.canAutoBan = true;
15
+ }
16
+ gatherRegexes() {
17
+ const regexes = this.getVariable("ageregexes", []);
18
+ return regexes.map(regex => ({
19
+ evaluatorName: this.name,
20
+ regex,
21
+ }));
22
+ }
23
+ validateVariables() {
24
+ const regexes = this.gatherRegexes().map(r => r.regex);
25
+ const results = [];
26
+ for (const regexVal of regexes) {
27
+ try {
28
+ new RegExp(regexVal);
29
+ const parsed = (0, regjsparser_1.parse)(regexVal, "i");
30
+ if (parsed.type !== "alternative") {
31
+ results.push({ severity: "error", message: `No capturing group found in regex: ${regexVal}` });
32
+ continue;
33
+ }
34
+ const capturingGroups = parsed.body.filter(part => part.type === "group" && part.behavior === "normal");
35
+ if (capturingGroups.length !== 1) {
36
+ results.push({ severity: "error", message: `Expected exactly one capturing group in regex: ${regexVal}` });
37
+ continue;
38
+ }
39
+ }
40
+ catch {
41
+ results.push({ severity: "error", message: `Invalid regex: ${regexVal}` });
42
+ }
43
+ }
44
+ return results;
45
+ }
46
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
+ preEvaluateComment(_event) {
48
+ return false;
49
+ }
50
+ getAgeFromPostTitle(title, regexes) {
51
+ for (const regex of regexes) {
52
+ const match = title.match(regex);
53
+ if (match) {
54
+ return parseInt(match[1]);
55
+ }
56
+ }
57
+ }
58
+ preEvaluatePost(post) {
59
+ if (!post.isNsfw()) {
60
+ return false;
61
+ }
62
+ if (post.crosspostParentId) {
63
+ return false;
64
+ }
65
+ const regexes = this.gatherRegexes().map(r => new RegExp(r.regex, r.flags));
66
+ return this.getAgeFromPostTitle(post.title, regexes) !== undefined;
67
+ }
68
+ preEvaluateUser(user) {
69
+ return user.commentKarma < 50;
70
+ }
71
+ evaluate(user) {
72
+ const nsfwPosts = this.getPosts({ since: (0, date_fns_1.subWeeks)(new Date(), 2) })
73
+ .filter(post => post.isNsfw() && !post.subredditName.toLowerCase().includes("roleplay") && !post.crosspostParentId);
74
+ const contentThreshold = this.getVariable("contentthreshold", 6);
75
+ this.banContentThreshold = contentThreshold;
76
+ if (nsfwPosts.length < 4) {
77
+ return false;
78
+ }
79
+ const regexes = this.gatherRegexes().map(r => new RegExp(r.regex, r.flags));
80
+ const agesFound = (0, lodash_1.uniq)((0, lodash_1.compact)(nsfwPosts.map(post => this.getAgeFromPostTitle(post.title, regexes))));
81
+ if (agesFound.length < 3) {
82
+ return false;
83
+ }
84
+ if (user.userDescription?.includes("shared") || user.userDescription?.includes("couple") || nsfwPosts.some(post => post.title.toLowerCase().includes("couple"))) {
85
+ this.canAutoBan = false;
86
+ }
87
+ this.addHitReason(`Inconsistent Age Bot: Found ${agesFound.length} different ages in ${nsfwPosts.length} posts`);
88
+ return true;
89
+ }
90
+ }
91
+ exports.EvaluateInconsistentAgeBot = EvaluateInconsistentAgeBot;
@@ -0,0 +1,17 @@
1
+ import { CommentCreate } from "@devvit/protos";
2
+ import { EvaluatorRegex, UserEvaluatorBase, ValidationIssue } from "./UserEvaluatorBase.js";
3
+ import { Post } from "@devvit/public-api";
4
+ import { UserExtended } from "@fsvreddit/fsv-devvit-helpers";
5
+ export declare class EvaluateInconsistentGenderBot extends UserEvaluatorBase {
6
+ name: string;
7
+ shortname: string;
8
+ banContentThreshold: number;
9
+ canAutoBan: boolean;
10
+ preEvaluateComment(_event: CommentCreate): boolean;
11
+ getGenderFromTitle(title: string): string | undefined;
12
+ gatherRegexes(): EvaluatorRegex[];
13
+ validateVariables(): ValidationIssue[];
14
+ preEvaluatePost(post: Post): boolean;
15
+ preEvaluateUser(user: UserExtended): boolean;
16
+ evaluate(user: UserExtended): boolean;
17
+ }